关于Java:如何打印”Hello World”?

How does this print “hello world”?

我发现了这个奇怪的地方:

1
2
for (long l = 4946144450195624l; l > 0; l >>= 5)
    System.out.print((char) (((l & 31 | 64) % 95) + 32));

输出:

1
hello world

这是怎么工作的?


数字4946144450195624适合64位,其二进制表示为:

1
 10001100100100111110111111110111101100011000010101000

程序从右到左为每5位组解码一个字符。

1
2
 00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
   d  |  l  |  r  |  o  |  w  |     |  o  |  l  |  l  |  e  |  h

5位编码

对于5位,可以表示2?=32个字符。英文字母表包含26个字母,这为32-26=6个符号留出了空间除了字母。有了这个编码方案,您可以拥有所有26(一个案例)的英文字母和6个符号(其中的空格)。

算法描述

for循环中的>>= 5从一个组跳到另一个组,然后5位组被隔离,并在句子l & 31中用掩码31?? = 11111?对数字进行运算。

现在,代码将5位值映射到相应的7位ASCII字符。这是一个棘手的部分,检查小写字母的二进制表示。下表中的字母:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
  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位打开),您可以OR5位用96(96?? = 1100000?)进行编码,这应该足以进行映射,但这对空间(darn space)不起作用。

现在我们知道,在处理空间的同时,必须特别注意其他角色。为此,代码打开第7位(但不是第6位)。提取的具有或64个64?? = 1000000?(l & 31 | 64的5位组。

到目前为止,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字符。

1
2
3
4
5
6
7
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位长值:

1
2
3
4
5
6
7
8
9
10
11
12
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脚本打印中间值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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));
}

这里是

1
2
3
4
5
6
7
8
9
10
11
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位(您可以找到每个字符的5位组合),然后将所有的位连接起来形成一个大数字。

正长整型是63位数字,大到足以容纳12个字符的加密形式。所以它足够大,可以容纳Hello word,但是对于较大的文本,您应该使用较大的数字,甚至一个大整数。

在一个应用程序中,我们希望通过短信传输可见的英语字符、波斯语字符和符号。如您所见,可能有32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127个值,可以用7位表示。

我们将每个UTF-8(16位)字符转换为7位,并获得超过56%的压缩比。所以我们可以用同样数量的短信发送两倍长度的短信。(不知怎么地,这里也发生了同样的事情)。


您得到的结果恰好是下面值的char表示

1
2
3
4
5
6
7
8
9
10
11
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是第i个编码值,范围为0-31。

正如你所说,最困难的部分是编码空间。小写英文字母在Unicode(和ASCII,以及大多数其他编码)中占据连续范围97-122,但空格为32。

为了克服这个问题,你用了一些算术。((x+64)%95)+32x + 96几乎相同(注意在本例中,按位或等于加法),但当x=31时,我们得到32


它打印"Hello World"的原因与此类似:

1
2
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));

但原因与此稍有不同:

1
2
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屋顶,很难看到这两个问题。主动的赏金给我在这里。我希望我有一个问题有其他的相关技术:(标签。

在mostly与Oracle database工作,所以我会使用一些Oracle知识两个interpret和解释:-)

简单的convert 4946144450195624binary号。对,我在使用一小functiondec2bin即所谓十进制-二进制。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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>

易于使用的功能是把两个二元价值

1
2
3
4
5
6
7
SQL> SELECT dec2bin(4946144450195624) FROM dual;

DEC2BIN(4946144450195624)
--------------------------------------------------------------------------------
10001100100100111110111111110111101100011000010101000

SQL>

现在我明白是5-bit转换。开始从左,右两个分组与五位在每个集团。我们得到:

1
100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000

我们会离开我终于与他三位国际比权。因为,我们的仇恨总53位的二进制转换。

1
2
3
4
5
6
7
SQL> SELECT LENGTH(dec2bin(4946144450195624)) FROM dual;

LENGTH(DEC2BIN(4946144450195624))
---------------------------------
                               53

SQL>

hello world总有11个字符(包括空间),所以我们需要添加的2比特的最后两个集团,我们是完全的左后与3位分组。

所以,现在我们有:

1
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000

现在,我们需要它convert 7位ASCII值。的特点是简单的,我们需要看到的是第六和第七位。添加两个11每5位以上两个集团的离开。

这是给予:

1
1100100|1101100|1110010|1101111|1110111|1111111|1101111|1101100|1101100|1100101|1101000

简单的interpret的二进制值,将使用binary to decimal conversion function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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>

简单的面貌,每个二元价值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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>

简单的看他们是什么人物。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
--------- --------- --------- --------- --------- --------- --------- --------- --------- --------- ---------
d         l         r         o         w         ?         o         l         l         e         h

SQL>

所以,我们怎么得到的输出?

D L r o w?O L E H

这是凯蒂吗?世界在反转。唯一的问题是空间。冰阱和合理的城市"explained higuaro在他的回答。在honestly不能interpret空间形式公布自己的第一届广交会上,直到在他的《解释给定的答案。


我发现斯莱特利的代码更容易吗?当被理解为PHP作为follows:

1
2
3
4
5
6
7
8
<?php

$result=0;
$bignum = 4946144450195624;
for (; $bignum > 0; $bignum >>= 5){
    $result = (( $bignum & 31 | 64) % 95) + 32;
    echo chr($result);
}

看到活着的代码


out.println((char)(((L &;31 | 64)95 %)* + 32 1002439 1002439));

使它灯头:3