Boh*_*ian 163 java string bit-shift
我发现这个怪异:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Run Code Online (Sandbox Code Playgroud)
输出:
hello world
Run Code Online (Sandbox Code Playgroud)
这是如何运作的?
hig*_*aro 255
该数字4946144450195624适合64位,其二进制表示为:
10001100100100111110111111110111101100011000010101000
Run Code Online (Sandbox Code Playgroud)
程序从右到左解码每个5位组的字符
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
d | l | r | o | w | | o | l | l | e | h
Run Code Online (Sandbox Code Playgroud)
对于5位,可以表示2⁵= 32个字符.英文字母包含26个字母,除了字母外,还留有32 - 26 = 6个符号的空间.通过这种编纂方案,您可以拥有所有26个(一个案例)英文字母和6个符号(其中包含空格).
的>>= 5在for循环跳跃组与组的,则该5位组被分离AND运算与掩模的数目31?? = 11111?在句子l & 31
现在,代码将5位值映射到其对应的7位ascii字符.这是棘手的部分,请检查下表中小写字母的二进制表示:
ascii | ascii | ascii | algorithm
character | decimal value | binary value | 5-bit codification
--------------------------------------------------------------
space | 32 | 0100000 | 11111
a | 97 | 1100001 | 00001
b | 98 | 1100010 | 00010
c | 99 | 1100011 | 00011
d | 100 | 1100100 | 00100
e | 101 | 1100101 | 00101
f | 102 | 1100110 | 00110
g | 103 | 1100111 | 00111
h | 104 | 1101000 | 01000
i | 105 | 1101001 | 01001
j | 106 | 1101010 | 01010
k | 107 | 1101011 | 01011
l | 108 | 1101100 | 01100
m | 109 | 1101101 | 01101
n | 110 | 1101110 | 01110
o | 111 | 1101111 | 01111
p | 112 | 1110000 | 10000
q | 113 | 1110001 | 10001
r | 114 | 1110010 | 10010
s | 115 | 1110011 | 10011
t | 116 | 1110100 | 10100
u | 117 | 1110101 | 10101
v | 118 | 1110110 | 10110
w | 119 | 1110111 | 10111
x | 120 | 1111000 | 11000
y | 121 | 1111001 | 11001
z | 122 | 1111010 | 11010
Run Code Online (Sandbox Code Playgroud)
在这里你可以看到我们要映射的ascii字符以第7和第6位set(11xxxxx?)开头(空格除外,它只有第6位),你可以OR用96(96?? = 1100000?)进行5位编码,那应该是足以进行映射,但这对空间不起作用(darn space!)
现在我们知道必须特别注意与其他角色同时处理空间.为实现此目的,代码使用OR 64 64?? = 1000000?(l & 31 | 64)将提取的5位组的第7位打开(但不是第6位).
到目前为止,5位组的形式是:( 10xxxxx?空间将是1011111? = 95??).如果我们可以将空间映射到不0影响其他值,那么我们可以打开第6位,这应该是全部.这是mod 95部件发挥作用,空间是1011111? = 95??,使用mod操作(l & 31 | 64) % 95)只有空间返回0,在此之后,代码通过添加32?? = 100000?
到先前的结果将第6位打开,将((l & 31 | 64) % 95) + 32)5位值转换为有效的ascii字符
isolates 5 bits --+ +---- takes 'space' (and only 'space') back to 0
| |
v v
(l & 31 | 64) % 95) + 32
^ ^
turns the | |
7th bit on ------+ +--- turns the 6th bit on
Run Code Online (Sandbox Code Playgroud)
下面的代码执行逆过程,给定一个小写字符串(最多12个字符),返回可以与OP代码一起使用的64位长值:
public class D {
public static void main(String... args) {
String v = "hello test";
int len = Math.min(12, v.length());
long res = 0L;
for (int i = 0; i < len; i++) {
long c = (long) v.charAt(i) & 31;
res |= ((((31 - c) / 31) * 31) | c) << 5 * i;
}
System.out.println(res);
}
}
Run Code Online (Sandbox Code Playgroud)
Jay*_*yan 39
为上述答案添加一些价值.以下groovy脚本打印中间值.
String getBits(long l) {
return Long.toBinaryString(l).padLeft(8,'0');
}
for (long l = 4946144450195624l; l > 0; l >>= 5){
println ''
print String.valueOf(l).toString().padLeft(16,'0')
print '|'+ getBits((l & 31 ))
print '|'+ getBits(((l & 31 | 64)))
print '|'+ getBits(((l & 31 | 64) % 95))
print '|'+ getBits(((l & 31 | 64) % 95 + 32))
print '|';
System.out.print((char) (((l & 31 | 64) % 95) + 32));
}
Run Code Online (Sandbox Code Playgroud)
这里是
4946144450195624|00001000|01001000|01001000|01101000|h
0154567014068613|00000101|01000101|01000101|01100101|e
0004830219189644|00001100|01001100|01001100|01101100|l
0000150944349676|00001100|01001100|01001100|01101100|l
0000004717010927|00001111|01001111|01001111|01101111|o
0000000147406591|00011111|01011111|00000000|00100000|
0000000004606455|00010111|01010111|01010111|01110111|w
0000000000143951|00001111|01001111|01001111|01101111|o
0000000000004498|00010010|01010010|01010010|01110010|r
0000000000000140|00001100|01001100|01001100|01101100|l
0000000000000004|00000100|01000100|01000100|01100100|d
Run Code Online (Sandbox Code Playgroud)
Ami*_*deh 26
有趣!
可见的标准ASCII字符的范围为32到127.
这就是你在那里看到32和95(127 - 32)的原因.
实际上,每个字符在这里被映射到5位(你可以找到每个字符的5位组合),然后所有位被连接起来形成一个大数字.
正长数是63位数,大到足以容纳12个字符的加密形式.所以它足以容纳Hello word,但对于较大的文本,你应该使用更大的数字,甚至是BigInteger.
在一个应用程序中,我们希望通过SMS传输可见的英文字符,波斯字符和符号.如您所见,有32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127可能的值,可以用7位表示.
我们将每个UTF-8(16位)字符转换为7位,并获得超过56%的压缩比.因此,我们可以在相同数量的SMS中发送长度为两倍的文本.(这在某种程度上发生了同样的事情).
Vik*_*s V 17
您得到的结果恰好char代表了以下值
104 -> h
101 -> e
108 -> l
108 -> l
111 -> o
32 -> (space)
119 -> w
111 -> o
114 -> r
108 -> l
100 -> d
Run Code Online (Sandbox Code Playgroud)
Ale*_*sky 16
您已将字符编码为5位值,并将其中的11个打包为64位长.
(packedValues >> 5*i) & 31 是第i个编码值,范围为0-31.
正如你所说,困难的部分是编码空间.小写英文字母在Unicode(和ascii,以及大多数其他编码)中占据连续范围97-122,但空间为32.
为了克服这个问题,你使用了一些算法.((x+64)%95)+32几乎相同x + 96(注意在这种情况下,按位OR如何相当于加法),但是当x = 31时,我们得到32.
由于类似的原因,它打印"hello world":
for (int k=1587463874; k>0; k>>=3)
System.out.print((char) (100 + Math.pow(2,2*(((k&7^1)-1)>>3 + 1) + (k&7&3)) + 10*((k&7)>>2) + (((k&7)-7)>>3) + 1 - ((-(k&7^5)>>3) + 1)*80));
Run Code Online (Sandbox Code Playgroud)
但有一个不同的原因:
for (int k=2011378; k>0; k>>=2)
System.out.print((char) (110 + Math.pow(2,2*(((k^1)-1)>>21 + 1) + (k&3)) - ((k&8192)/8192 + 7.9*(-(k^1964)>>21) - .1*(-((k&35)^35)>>21) + .3*(-((k&120)^120)>>21) + (-((k|7)^7)>>21) + 9.1)*10));
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
6499 次 |
| 最近记录: |