这是如何打印“你好世界”?
我发现了这个怪事:
for (long l = 4946144450195624l; l > 0; l >>= 5) System.out.print((char) (((l & 31 | 64) % 95) + 32));
输出:
hello world
这个怎么用?
数字4946144450195624
适合64位,其二进制表示是:
10001100100100111110111111110111101100011000010101000
程序从右到左为每个5位组解码一个字符
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000 d | l | r | o | w | | o | l | l | e | h
5位编码
对于5位,可以表示2 5 = 32个字符。 英文字母包含26个字母,这就为32 – 26 = 6个符号留下了空间。 有了这个编纂scheme,你可以有26个(1个)英文字母和6个符号(其中有空格)。
algorithm描述
for循环中的>>= 5
从组跳转,然后5位组与31₁₀ = 11111₂
l & 31
的掩码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位开始( 11xxxxx₂
)(空格除外,只有第6位),可以用96
( 96
96₁₀ = 1100000₂
),这应该足以做映射,但是这对空间不起作用(补空间!)
现在我们知道,要特别注意与其他angular色同时处理空间。 为了达到这个目的,代码用OR 64 64 64₁₀ = 1000000₂
( l & 31 | 64
)将提取的5位组上的第7位(但不是第6位)打开。
到目前为止,5位组的forms是: 10xxxxx₂
(空格将是1011111₂ = 95₁₀
)。 如果我们可以将空间映射到0
而不影响其他值,那么我们就可以把第6位变成应该是全部。 这里是mod 95
部分的作用,空间是1011111₂ = 95₁₀
,使用mod操作(l & 31 | 64) % 95)
只有空间回到0
,此后代码将第6位开启((l & 31 | 64) % 95) + 32)
32₁₀ = 100000₂
((l & 31 | 64) % 95) + 32)
将((l & 31 | 64) % 95) + 32)
位的值转换成一个有效的ASCII字符
isolates 5 bits --+ +---- takes 'space' (and only 'space') back to 0 | | vv (l & 31 | 64) % 95) + 32 ^ ^ turns the | | 7th bit on ------+ +--- turns the 6th bit on
下面的代码在给定一个小写string(最多12个字符)的情况下进行反向处理,返回可以与OP代码一起使用的64位long值:
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
有趣!
标准的ASCII字符是可见的,范围在32到127之间。
这就是为什么你在那里看到32和95(127 – 32)。
实际上,每个字符在这里映射为5位(可以find每个字符的5位组合),然后将所有位连接起来形成一个大的数字。
长整数是63位数字,足以容纳12个字符的encryptionforms。 所以它足够大,可以容纳Hello word
,但是对于较大的文本,您可以使用较大的数字,甚至是BigInteger。
在一个应用程序中,我们想通过短信传送可见的英文字符,波斯文字符和符号。 正如你所看到的,有32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127
可能的值,可以用7位表示。
我们将每个UTF-8(16位)字符转换为7位,并获得56%以上的压缩比。 所以我们可以在相同数量的短信中发送两次长度的短信。 (这在某种程度上是同样的事情发生在这里)。
你会得到一个恰好是下面值的char
表示的结果
104 -> h 101 -> e 108 -> l 108 -> l 111 -> o 32 -> (space) 119 -> w 111 -> o 114 -> r 108 -> l 100 -> d
您已将字符编码为5位值,并将其中的11个字符打包为64位长。
(packedValues >> 5*i) & 31
是范围为0-31的第i个编码值。
如你所说,困难的部分是编码空间。 小写英文字母占用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));
但有一个不同的原因比这个:
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));
没有Oracle
标签,很难看到这个问题。 积极的赏金把我带到了这里。 我希望这个问题也有其他相关的技术标签:-(
我主要使用Oracle database
,所以我会用一些Oracle
知识来解释和解释:-)
我们把数字4946144450195624
转换成binary
。 为此,我使用了一个叫做dec2bin的小function
即十进制到二进制 。
SQL> CREATE OR REPLACE FUNCTION dec2bin (N in number) RETURN varchar2 IS 2 binval varchar2(64); 3 N2 number := N; 4 BEGIN 5 while ( N2 > 0 ) loop 6 binval := mod(N2, 2) || binval; 7 N2 := trunc( N2 / 2 ); 8 end loop; 9 return binval; 10 END dec2bin; 11 / Function created. SQL> show errors No errors. SQL>
让我们使用函数来获取二进制值 –
SQL> SELECT dec2bin(4946144450195624) FROM dual; DEC2BIN(4946144450195624) -------------------------------------------------------------------------------- 10001100100100111110111111110111101100011000010101000 SQL>
现在是5-bit
转换。 从右到左开始分组,每组5位数。 我们得到: –
100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
我们最终只剩下3位数字,他在右边结束。 因为我们在二进制转换中总共有53位数字。
SQL> SELECT LENGTH(dec2bin(4946144450195624)) FROM dual; LENGTH(DEC2BIN(4946144450195624)) --------------------------------- 53 SQL>
hello world
总共有11个字符(包括空格),所以我们需要添加2位到最后一组,我们剩下的只有3位。
所以,现在我们有:
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
现在,我们需要将它转换为7位的ASCII值。 对于字符很容易,我们只需要设置第6和第7位。 向左上方的每个5位组添加11
。
这给了: –
1100100|1101100|1110010|1101111|1110111|1111111|1101111|1101100|1101100|1100101|1101000
让我们解释二进制值,我将使用binary to decimal conversion function
。
SQL> CREATE OR REPLACE FUNCTION bin2dec (binval in char) RETURN number IS 2 i number; 3 digits number; 4 result number := 0; 5 current_digit char(1); 6 current_digit_dec number; 7 BEGIN 8 digits := length(binval); 9 for i in 1..digits loop 10 current_digit := SUBSTR(binval, i, 1); 11 current_digit_dec := to_number(current_digit); 12 result := (result * 2) + current_digit_dec; 13 end loop; 14 return result; 15 END bin2dec; 16 / Function created. SQL> show errors; No errors. SQL>
让我们看看每个二进制值 –
SQL> set linesize 1000 SQL> SQL> SELECT bin2dec('1100100') val, 2 bin2dec('1101100') val, 3 bin2dec('1110010') val, 4 bin2dec('1101111') val, 5 bin2dec('1110111') val, 6 bin2dec('1111111') val, 7 bin2dec('1101111') val, 8 bin2dec('1101100') val, 9 bin2dec('1101100') val, 10 bin2dec('1100101') val, 11 bin2dec('1101000') val 12 FROM dual; VAL VAL VAL VAL VAL VAL VAL VAL VAL VAL VAL ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- ---------- 100 108 114 111 119 127 111 108 108 101 104 SQL>
让我们看看他们是什么字符: –
SQL> SELECT chr(bin2dec('1100100')) character, 2 chr(bin2dec('1101100')) character, 3 chr(bin2dec('1110010')) character, 4 chr(bin2dec('1101111')) character, 5 chr(bin2dec('1110111')) character, 6 chr(bin2dec('1111111')) character, 7 chr(bin2dec('1101111')) character, 8 chr(bin2dec('1101100')) character, 9 chr(bin2dec('1101100')) character, 10 chr(bin2dec('1100101')) character, 11 chr(bin2dec('1101000')) character 12 FROM dual; CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER CHARACTER --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- --------- dlrow ⌂ olleh SQL>
那么,我们在输出中得到了什么?
dlrow⌂olleh
这就是hello⌂world 。 唯一的问题是空间 。 @higuaro在他的回答中解释了这个理由。 我第一次尝试的时候,我实在不可能解释空间问题,直到我看到他的回答中给出的解释。
我发现这些代码在翻译成PHP时稍微容易理解,如下所示:
<?php $result=0; $bignum = 4946144450195624; for (; $bignum > 0; $bignum >>= 5){ $result = (( $bignum & 31 | 64) % 95) + 32; echo chr($result); }
查看实时代码
out.println((char)(((1&31 | 64)%95)+ 32/1002439 * 1002439));
为了做到这一点:3