Vous aimez ce que vous lisez sur ce blog ?
Envie d'aller plus loin avec véritable formation d'expertise en Java ?
Venez suivre ma formation Masterclasse Expertise Java !

"Même un développeur experimenté a besoin de continuer à apprendre. Et dans cette formation... j'ai appris beaucoup !" - A.G., Java Champion

Sessions intra-entreprises sur demande : contact[at]mokatech.net.
Inscrivez-vous vite !

Java Quiz #18

Que fait ce code ? (et pourquoi ?)

  1. class Koassah{public static void main(String[] args){\u0066or(int
  2. \u0020$:"m¾\"®Æ¤¾\u01b0¤´È:\"À¾\"ª´Ê\"\u0138iÀ¼¼¬¾Ê\"`\u0158".to\u0043h\u0061rArray()
  3. )System./*goto/*$/%\u0126//^\u002A\u002Fout.print((char)(($>>+
  4. (~'"'&'#'))+('<'>>('\\'/'.')/\u002Array.const(~1)\*\u002F)));}}
  5.  

Voilà un quiz où il ne suffira pas de copier/coller le code dans Eclipse pour donner la réponse... ;-)

Bon, je reconnais, ce code est assez abscons, mais dans la vie, on ne rencontre pas toujours des codes idéalement écrits. Il peut donc être utile de savoir quoi faire dans ce type de situation.

Procédons par étapes.

Tout d'abord, un peu de reformatage ne ferait pas de mal à un code aussi horrible :

  1. class Koassah{
  2. public static void main(String[] args){
  3. \u0066or(int\u0020$:"m¾\"®Æ¤¾\u01b0¤´È:\"À¾\"ª´Ê\"\u0138iÀ¼¼¬¾Ê\"`\u0158".to\u0043h\u0061rArray()) {
  4. System./*goto/*$/%\u0126//^\u002A\u002Fout.print((char)(($>>+(~'"'&'#'))+('<'>>('\\'/'.')/\u002Array.const(~1)\*\u002F)));
  5.   }
  6.   }
  7. }
  8.  

C'est mieux, mais ce n'est pas encore ça.

Mais n'oublions pas que, selon la JLS (3.2), tous les caractères saisis sous la forme d'une séquence Unicode sont remplacés avant toute autre étape de compilation par le caractère réel. Ici, nous avons clairement de quoi rendre plus lisible notre classe :

  1. class Koassah{
  2. public static void main(String[] args){
  3. for(int $:"m¾\"®Æ¤¾\u01b0¤´È:\"À¾\"ª´Ê\"\u0138iÀ¼¼¬¾Ê\"`\u0158".toCharArray()) {
  4. System./*goto/*$/%\u0126//^*/out.print((char)(($>>+(~'"'&'#'))+('<'>>('\\'/'.')/*rray.const(~1)\*/)));
  5. }
  6. }
  7. }

On découvre alors qu'il y avait des commentaires ! Supprimons-les derechef :

  1. class Koassah{
  2. public static void main(String[] args){
  3. for(int $:"m¾\"®Æ¤¾\u01b0¤´È:\"À¾\"ª´Ê\"\u0138iÀ¼¼¬¾Ê\"`\u0158".toCharArray()) {
  4. System.out.print((char)(($>>+(~'"'&'#'))+('<'>>('\\'/'.'))));
  5. }
  6. }
  7. }

Ici, petite surprise : on reconnaît bien une boucle for, mais sa variable s'appelle $ ! Et Java ne dit rien ! Et oui, d'après la JLS (3.8) (pour des raisons historiques, dit-elle) le caractère $ fait partie des caractères autorisés pour les identifiants.

Notons au passage que les autres symboles monétaires sont considérés comme des lettres ; on peut donc les utiliser dans des identificateurs :

  1. private String $Dollar; // Toutes ces lignes compilent sans erreur
  2. private String £Pound;
  3. private String €Euro;
  4. private String ¥Yen;
  5. private String ¢Cent;
  6. private String \u20a3Franc;
  7. private String \u20a4Lira;
  8. private String \u20aaNewShekel;
  9. private String \u20a7Peseta;
  10. private String \u20abDong;
  11. private String ¤Currency;
  12. private String \u03c0SmallGreekPi; // Les lettres des autres alphabets sont acceptées elles aussi...
  13. private String \u05d0HebraicAlef;
  14. private String \u2113RoundL;
  15. private String µMicro; // Même les éléments d'unités physiques sont utilisables !
  16. private String \u2126Ohm;

D'autres symboles ne sont pas considérés comme des lettres, et le compilateur Java les refuse :

  1. private String ©Copy; // Toutes ces lignes compilent avec une erreur "illegal character"
  2. private String ®Registered;
  3. private String §Paragraph;
  4. private String ¬Not;
  5. private String ±MoreOrLess;
  6. private String ¦DashedVerticalBar;
  7. private String ¡InvertedExclamation;
  8. private String ¿InvertedQuestion;
  9. private String \u2122TradeMark;
  10. private String \u221aSquareRoot;
  11. private String \u221eInfinite;
  12. private String \u2640Female;
  13. private String \u2642Male;
  14. private String \u263aSmile;
  15. private String \u2105CareOf;
  16. private String \u2116Number;

Renommons donc notre variable $ avec un nom un brin plus compréhensible pour un humain, et profitons-en pour agrémenter le code de quelques espaces ici et là :

  1. class Koassah {
  2. public static void main(String[] args) {
  3. for (int caractere : "m¾\"®Æ¤¾\u01b0¤´È:\"À¾\"ª´Ê\"\u0138iÀ¼¼¬¾Ê\"`\u0158".toCharArray()) {
  4. System.out.print((char) ((caractere >> + (~ '"' & '#')) + ('<' >> ('\\' / '.'))));
  5. }
  6. }
  7. }

Voilà qui devient plus clair : on prend une chaîne de caractères, et, pour chacun des caractères, on applique un petit traitement numérique sur son code Unicode. Notons au passage que toCharArray() renvoie un tableau de caractères, mais que la variable de boucle est déclarée de type int. Sans que cela ne provoque d'erreur de la part du compilateur.

Sinon, une idée sur ce traitement numérique ?

Déjà, si on remplaçait les caractères du calcul par leur code, puisqu'en fait, on fait des calculs sur des entiers, ce serait plus clair :

  1. class Koassah {
  2. public static void main(String[] args) {
  3. for (int caractere : "m¾\"®Æ¤¾\u01b0¤´È:\"À¾\"ª´Ê\"\u0138iÀ¼¼¬¾Ê\"`\u0158".toCharArray()) {
  4. System.out.print((char) ((caractere >> + (~ 34 & 35)) + (60 >> (92 / 46))));
  5. }
  6. }
  7. }

Et la simplification devient alors aisée :

  1. class Koassah {
  2. public static void main(String[] args) {
  3. String message = "m¾\"®Æ¤¾\u01b0¤´È:\"À¾\"ª´Ê\"\u0138iÀ¼¼¬¾Ê\"`\u0158";
  4.  
  5. for (int caractere : message.toCharArray()) {
  6. System.out.print((char) ((caractere >> 1) + 15));
  7. }
  8. }
  9. }

Du coup, il est facile de savoir ce qu'affiche ce programme, en prenant le code de chaque caractère, en le transformant, et en en déduisant le caractère transformé :

109 -->  69 = E
190 --> 110 = n
 34 -->  32 =  
174 --> 102 = f
198 --> 114 = r
164 -->  97 = a
190 --> 110 = n
432 --> 231 = ç
164 -->  97 = a
180 --> 105 = i
200 --> 115 = s
 58 -->  44 = ,
 34 -->  32 =  
192 --> 111 = o
190 --> 110 = n
 34 -->  32 =  
170 --> 100 = d
180 --> 105 = i
202 --> 116 = t
 34 -->  32 =  
312 --> 171 = «
105 -->  67 = C
192 --> 111 = o
188 --> 109 = m
188 --> 109 = m
172 --> 101 = e
190 --> 110 = n
202 --> 116 = t
 34 -->  32 =  
 96 -->  63 = ?
344 --> 187 = »

Autrement dit : "En français, on dit «Comment ?»" (hé oui, dire « Koassah ?», ce n'est guère distingué... :-) )

Finalement, ce n'était pas si difficile !

Mais cela nous a permis de nous souvenir de trois choses :

  • Le compilateur remplace automatiquement les séquences Unicode dans le code Java, toujours avant d'effectuer quoi que ce soit d'autre.
  • Le caractère $ est un caractère valide (quoique peu recommandable) au sein d'un identifiant Java.
  • Malgré leur typage différent, les caractères sont manipulables comme des types entiers.

Et voilà !


Commentaires

1. Le vendredi 17 octobre 2008, 11:41 par Histrion

Ce qu'il fait ? Mal au crâne. Pourquoi ? :)

Ajouter un commentaire

Le code HTML est affiché comme du texte et les adresses web sont automatiquement transformées.