我发现了这个怪事:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
输出:
hello world
这是怎么工作的?
有意思!
可见的标准ASCII字符范围为32到127。
这就是为什么你会看到32和95(127-32)。
实际上每个字符都被映射到5位(你可以找到每个字符的5位组合),然后所有的位被级联形成一个大数。
正长是63位的数字,大到足以容纳12个字符的加密形式。因此它足够大,可以容纳hello word
,但是对于较大的文本,您应该使用较大的数字,甚至是biginteger。
在一个应用程序中,我们希望通过SMS传输可见的英文字符、波斯语字符和符号。如您所见,有32(波斯语字符数)+95(英文字符和标准可见符号数)=127
可能的值,可以用7位表示。
我们将每个UTF-8(16位)字符转换为7位,获得了56%以上的压缩比。这样我们就可以在相同数量的sms中发送两倍长度的文本。(不知怎的,这里也发生了同样的事情)。
为上述答案增加了一些价值。下面的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
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位,可以表示2=32个字符。英文字母表包含26个字母,这就为32-26=6个符号留下了空间。有了这个编码方案,你可以有所有26个(一个案例)英文字母和6个符号(空格在其中)。
for循环中的>>=5
从一个组跳到另一个组,然后5位组被隔离,并且在句子L&31
中使用掩码31=11111
的数字
现在,代码将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
)(space除外,它只有第6位开启),您可以使用96
(96=11000002014
)对5位编码进行或
,这应该足以进行映射,但对于space(该死的space!)
现在我们知道,必须特别注意处理空间的同时,其他字符。为此,该代码将提取的5位组的第7位(但不是第6位)打开,并使用OR 6464=1000000220
(L&31 64
)。
到目前为止,5位组的形式为:10xxxxx*
(空格为1011111*=95
)。如果我们可以将空间映射到0
而不影响其他值,那么我们可以打开第6位,这应该是全部。下面是mod 95
部分的作用,空格是1011111
,使用mod操作(l&31 64)%95)
只有空格返回到0
,在此之后,代码通过将32=10000042
添加到前一个结果((l&31 64)%95)+32)
将5位值转换为有效的ascii字符来打开第6位
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
下面的代码html" target="_blank">执行相反的过程,给定一个小写字符串(最多12个字符),返回可以与操作的代码一起使用的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);
}
}
最近我一直在使用带有大量数字的循环来打印: 我知道这是一种非常愚蠢的方法,但我还从未在Java中使用过任何计时器库。如何修改上述内容,使其每3秒打印一次?
下面是我在网上找到的一些代码: 此代码将打印到屏幕上;你可以看到它在这里运行。我可以清楚地看到写的,但它是反向的。这段代码是如何工作的?这是如何编译的呢? 编辑:我在IntellIJ中尝试了这段代码,它工作得很好。但是,由于某些原因,它在notepad++和cmd中不起作用。我还没有找到解决方法,所以如果有人找到了,请在下面评论。
本文向大家介绍TypeScript 你好,世界,包括了TypeScript 你好,世界的使用技巧和注意事项,需要的朋友参考一下 示例 这里我们有一个类 Greeter,它有一个构造函数和一个 greet 方法。我们可以使用 new 关键字构造一个类的实例,然后传入一个字符串,我们希望 greet 方法输出到控制台。Greeter 类的实例存储在 Greeter 变量中,然后我们调用 greet 方
本文向大家介绍qml 你好,世界,包括了qml 你好,世界的使用技巧和注意事项,需要的朋友参考一下 示例 一个简单的应用程序在窗口中心显示文本“ Hello World”。
本文向大家介绍xaml 你好,世界,包括了xaml 你好,世界的使用技巧和注意事项,需要的朋友参考一下 示例 这是WPF中XAML页面的简单示例。它由XAML中最常见的元素a Grid,aTextBlock和aButton组成。 语法 描述 <Window> 根容器,用于承载可视化数据并使用户能够与之交互的内容。WPF窗口是XAML(.xaml)文件(其中元素为根)和CodeBehind(.cs)
本文向大家介绍processing 你好,世界,包括了processing 你好,世界的使用技巧和注意事项,需要的朋友参考一下 示例 编写处理代码的最简单方法是简单地调用一系列函数。在处理编辑器中按运行按钮,处理将运行您的代码。这是一个例子: 此代码创建一个200x200窗口,绘制一个蓝色背景,将填充颜色更改为绿色,然后在屏幕中间绘制一个圆圈。 但是,大多数处理草图将使用预定义的setup()和d