这个打印“你好世界”怎么样?5 位编纂算法描述
我发现了这个奇怪的地方:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
输出:
hello world
这是如何工作的?
我发现了这个奇怪的地方:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
输出:
hello world
这是如何工作的?
该数字拟合 64 位,其二进制表示形式为:4946144450195624
10001100100100111110111111110111101100011000010101000
该程序从右到左为每个5位组解码一个字符
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
d | l | r | o | w | | o | l | l | e | h
对于 5 位,可以表示 2⁵ = 32 个字符。英语字母表包含26个字母,这为32 - 26 = 除字母外的6个符号留出了空间。使用此编纂方案,您可以拥有所有26个(一种情况)英文字母和6个符号(其中空格)。
for 循环中的 从一个组跳到另一个组,然后 5 位组被隔离,用句子中的掩码对数字进行 AND。>>= 5
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
在这里,您可以看到我们要映射的 ASCII 字符以第 7 位和第 6 位集 () 开头()(空格除外,它只打开了第 6 位)。你可以用()进行5位编码,这应该足以进行映射,但这对空间不起作用(的空间!11xxxxx₂
OR
96
96₁₀ = 1100000₂
现在我们知道,必须特别注意与其他字符同时处理空间。为此,代码在提取的 5 位组上使用 OR 64 () 打开第 7 位(但不是第 6 位)。64₁₀ = 1000000₂
l & 31 | 64
到目前为止,5 位组的形式为:(空格为 )。10xxxxx₂
1011111₂ = 95₁₀
如果我们可以将空间映射到不受影响的其他值,那么我们可以打开第6位,这应该就是全部。0
这是该角色的作用。空间是,使用模运算。只有空格返回到 ,在此之后,代码通过添加到上一个结果来打开第 6 位,将 5 位值转换为有效的 ASCII 字符。mod 95
1011111₂ = 95₁₀
(l & 31 | 64) % 95)
0
32₁₀ = 100000₂
((l & 31 | 64) % 95) + 32)
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
下面的代码在给定小写字符串(最多 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);
}
}
以下 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));
}
在这里:
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