web-dev-qa-db-fra.com

Pourquoi ce code, écrit à l'envers, affiche-t-il "Hello World!"

Voici du code que j'ai trouvé sur Internet:

class M‮{public static void main(String[]a‭){System.out.print(new char[]
{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}    

Ce code imprime Hello World! sur l'écran; vous pouvez le voir courir ici . Je vois clairement public static void main écrit, mais c'est à l'envers. Comment fonctionne ce code? Comment cela se compile-t-il?

Edit: J'ai essayé ce code dans IntellIJ, et cela fonctionne bien. Cependant, pour une raison quelconque, cela ne fonctionne pas dans notepad ++, avec cmd. Je n'ai toujours pas trouvé de solution à cela, alors si quelqu'un le fait, commentez ci-dessous.

252

Il y a des caractères invisibles qui modifient l'affichage du code. Dans Intellij, vous pouvez les trouver en copiant-collant le code dans une chaîne vide (""), qui les remplace par des échappements Unicode, en supprimant leurs effets et en affichant l'ordre affiché par le compilateur.

Voici le résultat de ce copier-coller:

"class M\u202E{public static void main(String[]a\u202D){System.out.print(new char[]\n"+
        "{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}   "

Les caractères du code source sont stockés dans cet ordre et le compilateur les considère comme étant dans cet ordre, mais ils sont affichés différemment.

Notez le caractère \u202E, qui est une substitution de droite à gauche, démarrant un bloc où tous les caractères sont obligatoirement affichés de droite à gauche, et le \u202D, qui est un gauche -right override, en commençant par un bloc imbriqué où tous les caractères sont forcés dans l'ordre de gauche à droite, en remplaçant le premier remplacement.

Ainsi, lorsqu’il affiche le code original, class M s’affiche normalement, mais le \u202E inverse l’ordre d’affichage de tous les éléments jusqu’à la \u202D, qui inverse tout à nouveau. (Formellement, tout du \u202D au terminateur de ligne est inversé deux fois, une en raison de \u202D et une fois avec le reste du texte inversé en raison de \u202E, ce qui explique pourquoi le texte apparaît au milieu de la ligne au lieu de la fin.) La directionnalité de la ligne suivante est gérée indépendamment de la première en raison de la terminaison de ligne. {'H','e','l','l','o',' ','W','o','r','l','d','!'});}} s'affiche donc normalement.

Pour l'algorithme bidirectionnel Unicode complet (extrêmement complexe, long de plusieurs dizaines de pages), voir Annexe Unicode Standard n ° 9 .

245
Davis Broda

Cela semble différent à cause de algorithme bidirectionnel Unicode . Il existe deux caractères invisibles de RLO et de LRO utilisés par l'algorithme Unicode bidirectionnel pour modifier l'aspect visuel des caractères imbriqués entre ces deux méta-caractères.

Le résultat est que visuellement , ils regardent dans l'ordre inverse, mais les caractères réels en mémoire ne sont pas inversés. Vous pouvez analyser les résultats ici . Le compilateur Java ignorera RLO et LRO et les traitera comme des espaces, raison pour laquelle le code est compilé.

Remarque 1: Cet algorithme est utilisé par les éditeurs de texte et les navigateurs Web pour afficher simultanément des caractères simultanément les caractères LTR (anglais) et RTL (par exemple, arabe, hébreu) ​​- d'où un sens "bi". . Vous pouvez en savoir plus sur l’algorithme bidirectionnel à l’Unicode site web .
Note 2: Le comportement exact de LRO et de RLO est défini dans section 2.2 de l'algorithme.

43
James Lawson

Le caractère U+202E reflète le code de droite à gauche, il est cependant très malin. Est caché à partir du M,

"class M\u202E{..."

Comment ai-je trouvé la magie derrière tout ça?

Au début, quand j’ai vu la question difficile, c’est "une sorte de blague, de perdre du temps à quelqu'un d’autre", mais ensuite, j’ai ouvert mon IDE ("IntelliJ"), créé une classe, et passé le code ... et compilé !!! Alors, j’ai regardé mieux et j’ai vu que le "vide statique public" était en arrière, j’y suis donc allé avec le curseur et ai effacé quelques caractères ... Et qu'est-ce qui se passe? Les caractères ont commencé à s'effacer en arrière , alors, je pensais que mmm .... rare ... je dois l'exécuter ... Je passe donc à l'exécution le programme, mais je devais d’abord le sauvegarder ... et c’était quand je l’ai trouvé! . Je ne pouvais pas enregistrer le fichier car mon IDE disait qu'il y avait un encodage différent pour certains caractères et me indiquait où il se trouvait , Je lance donc une recherche dans Google pour des caractères spéciaux qui pourraient faire le travail, et c'est tout :)

Un peu sur

l'algorithme bidirectionnel Unicode, et U+202E impliqué, un bref expliquer :

La norme Unicode prescrit un ordre de représentation en mémoire appelé ordre logique. Lorsque le texte est présenté sous forme de lignes horizontales, la plupart des scripts affichent les caractères de gauche à droite. Cependant, il existe plusieurs scripts (tels que l'arabe ou l'hébreu) ​​dans lesquels l'ordre naturel d'affichage du texte horizontal à l'écran est de droite à gauche. Si tout le texte a une direction horizontale uniforme, l'ordre du texte à afficher est sans ambiguïté.

Toutefois, comme ces scripts de droite à gauche utilisent des chiffres écrits de gauche à droite, le texte est en fait bidirectionnel: mélange de texte de droite à gauche et de gauche à droite. Outre les chiffres, les mots incorporés en anglais et d'autres scripts sont également écrits de gauche à droite, produisant également un texte bidirectionnel. Sans une spécification claire, des ambiguïtés peuvent survenir lors de la détermination de l'ordre des caractères affichés lorsque la direction horizontale du texte n'est pas uniforme.

Cette annexe décrit l’algorithme utilisé pour déterminer la directionnalité du texte Unicode bidirectionnel. L'algorithme étend le modèle implicite actuellement utilisé par un certain nombre d'implémentations existantes et ajoute des caractères de formatage explicites pour des circonstances particulières. Dans la plupart des cas, il n'est pas nécessaire d'inclure des informations supplémentaires dans le texte pour obtenir un ordre d'affichage correct.

Cependant, dans le cas d'un texte bidirectionnel, il existe des circonstances dans lesquelles un ordre bidirectionnel implicite n'est pas suffisant pour produire un texte compréhensible. Pour traiter ces cas, un ensemble minimal de caractères de formatage directionnel est défini pour contrôler l'ordre des caractères lors du rendu. Cela permet un contrôle précis de l'ordre d'affichage pour un échange lisible et garantit que le texte brut utilisé pour des éléments simples tels que des noms de fichiers ou des étiquettes peut toujours être correctement commandé pour l'affichage.

Pourquoi créer un algorithme comme this ?

l'algorithme bidi peut restituer une séquence de caractères arabes ou hébreux les uns après les autres de droite à gauche.

P.S .: Je sais que ce n'est pas la meilleure réponse, mais c'était amusant de résoudre le problème en premier: P

Chapitre 3 de la spécification de langue fournit une explication en décrivant en détail comment la traduction lexicale est effectuée pour un programme Java. Ce qui compte le plus pour la question:

Les programmes sont écrits en Unicode (§3.1) , mais des traductions lexicales sont fournies (§3.2) afin que les échappements Unicode (§3.3) puissent être utilisés pour inclure tout caractère Unicode utilisant uniquement les caractères ASCII.

Ainsi, un programme est écrit en caractères Unicode et l'auteur peut les échapper en utilisant \uxxxx au cas où l'encodage de fichier ne prend pas en charge le caractère Unicode, auquel cas il est traduit en caractère approprié. Un des caractères Unicode présent dans ce cas est \u202E. Cela ne s'affiche pas dans l'extrait de code, mais si vous essayez de changer l'encodage du navigateur, les caractères masqués peuvent apparaître.

Par conséquent, la traduction lexicale entraîne la déclaration de classe:

class M\u202E{

ce qui signifie que l'identifiant de classe est M\u202E. Le spécification considère cela comme un identificateur valide:

Identifier:
    IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
IdentifierChars:
    JavaLetter {JavaLetterOrDigit}

Une "lettre ou chiffre Java" est un caractère pour lequel la méthode Character.isJavaIdentifierPart(int) renvoie true.

4
manouti