J'ai découvert cette bizarrerie:
for (long l = 4946144450195624l; l > 0; l >>= 5)
System.out.print((char) (((l & 31 | 64) % 95) + 32));
Production:
hello world
Comment cela marche-t-il?
Le nombre 4946144450195624
s'adapte à 64 bits, sa représentation binaire est:
10001100100100111110111111110111101100011000010101000
Le programme décode un caractère pour chaque groupe de 5 bits, de droite à gauche
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
d | l | r | o | w | | o | l | l | e | h
Pour 5 bits, il est possible de représenter 2⁵ = 32 caractères. L'alphabet anglais contient 26 lettres, ce qui laisse de la place pour 32 - 26 = 6 symboles en dehors des lettres. Avec ce schéma de codification, vous pouvez avoir les 26 lettres anglaises (un cas) et 6 symboles (en étant un espace parmi eux).
Le >>= 5
dans les sauts de boucle de groupe à groupe, puis le groupe de 5 bits est isolé ET le nombre avec le masque 31₁₀ = 11111₂
dans la phrase l & 31
Maintenant, le code mappe la valeur 5 bits à son caractère ascii 7 bits correspondant. C'est la partie délicate, vérifiez les représentations binaires pour les lettres de l'alphabet en minuscules dans le tableau suivant:
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
Ici, vous pouvez voir que les caractères ascii que nous voulons mapper commencent par les 7e et 6e bits (11xxxxx₂
) (sauf pour l'espace, qui n'a que le 6ème bit activé), vous pourriez OR
la codification 5 bits avec 96
(96₁₀ = 1100000₂
) et cela devrait être suffisant pour faire le mapping, mais cela ne fonctionnerait pas pour l'espace (sacrément d'espace!)
Nous savons maintenant qu'il faut prendre un soin particulier à traiter l'espace en même temps que les autres personnages. Pour ce faire, le code active le 7e bit (mais pas le 6e) sur le groupe 5 bits extrait avec un OR 64 64₁₀ = 1000000₂
(l & 31 | 64
).
Jusqu'à présent, le groupe 5 bits est de la forme: 10xxxxx₂
(l'espace serait 1011111₂ = 95₁₀
). Si nous pouvons mapper l'espace sur 0
sans affecter les autres valeurs, nous pouvons alors activer le 6ème bit et ce devrait être tout. Voici ce que le mod 95
une partie vient jouer, l'espace est 1011111₂ = 95₁₀
, en utilisant l'opération mod (l & 31 | 64) % 95)
seul l'espace revient à 0
, et après cela, le code active le 6e bit en ajoutant 32₁₀ = 100000₂
au résultat précédent, ((l & 31 | 64) % 95) + 32)
transformer la valeur de 5 bits en un caractère ascii valide
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
Le code suivant effectue le processus inverse, étant donné une chaîne en minuscules (max 12 caractères), renvoie la valeur longue de 64 bits qui pourrait être utilisée avec le code de l'OP:
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);
}
}
Ajouter de la valeur aux réponses ci-dessus. Le script groovy suivant imprime des valeurs intermédiaires.
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));
}
C'est ici
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
Intéressant!
Standard ASCII les caractères visibles sont compris entre 32 et 127.
C'est pourquoi vous voyez 32 et 95 (127 - 32) là-bas.
En fait, chaque caractère est mappé à 5 bits ici (vous pouvez trouver la combinaison de 5 bits pour chaque caractère), puis tous les bits sont concaténés pour former un grand nombre.
Les longs positifs sont des nombres de 63 bits, suffisamment grands pour contenir une forme cryptée de 12 caractères. Il est donc assez grand pour contenir Hello Word
, Mais pour les textes plus gros, vous devez utiliser des nombres plus grands, ou même un BigInteger.
Dans une application, nous voulions transférer des caractères anglais, des caractères persans et des symboles visibles via SMS. Comme vous le voyez, il existe 32 (number of Persian chars) + 95 (number of English characters and standard visible symbols) = 127
valeurs possibles, qui peuvent être représentées avec 7 bits.
Nous avons converti chaque caractère UTF-8 (16 bits) en 7 bits et avons obtenu un taux de compression supérieur à 56%. Nous avons donc pu envoyer des textes d'une longueur double dans le même nombre de SMS. (C'est en quelque sorte la même chose qui s'est produite ici).
Vous obtenez un résultat qui se trouve être char
représentation des valeurs ci-dessous
104 -> h
101 -> e
108 -> l
108 -> l
111 -> o
32 -> (space)
119 -> w
111 -> o
114 -> r
108 -> l
100 -> d
Vous avez encodé des caractères en valeurs 5 bits et en avez regroupé 11 en 64 bits.
(packedValues >> 5*i) & 31
est la ième valeur codée avec une plage de 0 à 31.
La partie difficile, comme vous le dites, est l'encodage de l'espace. Les lettres anglaises minuscules occupent la plage contiguë 97-122 en Unicode (et ascii, et la plupart des autres codages), mais l'espace est de 32.
Pour surmonter cela, vous avez utilisé de l'arithmétique. ((x+64)%95)+32
est presque identique à x + 96
(notez comment au niveau du bit OR est équivalent à l'addition, dans ce cas), mais lorsque x = 31, nous obtenons 32
.
Il imprime "bonjour le monde" pour une raison similaire:
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));
mais pour une raison quelque peu différente de celle-ci:
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));
Sans balise Oracle
, il était difficile de voir cette question. Une prime active m'a amené ici. Je souhaite que la question contienne également d'autres balises technologiques pertinentes :
Je travaille principalement avec Oracle database
, donc j'utiliserais des connaissances Oracle
pour interpréter et expliquer :-)
Convertissons le nombre 4946144450195624
en binary
. Pour cela, j'utilise un petit function
appelé dec2bin c'est-à-dire --- (décimal en binaire.
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>
Utilisons la fonction pour obtenir la valeur binaire -
SQL> SELECT dec2bin(4946144450195624) FROM dual;
DEC2BIN(4946144450195624)
--------------------------------------------------------------------------------
10001100100100111110111111110111101100011000010101000
SQL>
Maintenant, la capture est le 5-bit
conversion. Commencez à grouper de droite à gauche avec 5 chiffres dans chaque groupe. On a :-
100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
Nous nous retrouverions finalement avec juste chiffres dans la fin à droite. Parce que, nous avions un total de 53 chiffres dans la conversion binaire.
SQL> SELECT LENGTH(dec2bin(4946144450195624)) FROM dual;
LENGTH(DEC2BIN(4946144450195624))
---------------------------------
53
SQL>
hello world
total a 11 caractères (espace compris), nous devons donc ajouter 2 bits au dernier groupe où nous nous sommes retrouvés avec seulement 3 bits après le regroupement.
Donc, maintenant nous avons: -
00100|01100|10010|01111|10111|11111|01111|01100|01100|00101|01000
Maintenant, nous devons le convertir en valeur ascii 7 bits. Pour les personnages c'est facile, il suffit de régler les 6e et 7e bits. Ajouter 11
à chaque groupe de 5 bits au-dessus à gauche.
Ça donne :-
1100100|1101100|1110010|1101111|1110111|1111111|1101111|1101100|1101100|1100101|1101000
Interprétons les valeurs binaires, j'utiliserai 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>
Regardons chaque valeur binaire -
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>
Voyons quels sont ces personnages: -
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>
Alors, qu'obtenons-nous dans la sortie?
d l r o w ⌂ o l l e h
C'est hello⌂world à l'envers. Le seul problème est le espace. Et la raison est bien expliquée par @higuaro dans sa réponse. Honnêtement, je ne pouvais pas interpréter le problème d'espace moi-même à la première tentative, jusqu'à ce que je voie l'explication donnée dans sa réponse.
J'ai trouvé le code légèrement plus facile à comprendre lorsqu'il est traduit en PHP, comme suit:
<?php
$result=0;
$bignum = 4946144450195624;
for (; $bignum > 0; $bignum >>= 5){
$result = (( $bignum & 31 | 64) % 95) + 32;
echo chr($result);
}
Voir code en direct
out.println ((char) (((l & 31 | 64)% 95) + 32/1002439 * 1002439));
Pour faire des bouchons: 3