这是如何打印“你好世界”?

我发现了这个怪事:

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位),可以用9696 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