Pourquoi 2+ 40 est égal à 42?

360

J'ai été déconcerté lorsqu'un collègue m'a montré cette ligne d'alerte JavaScript 42.

alert(2+ 40);

Il s'avère rapidement que ce qui ressemble à un signe moins est en fait un caractère Unicode obscur avec une sémantique clairement différente.

Cela m'a laissé me demander pourquoi ce caractère ne produit pas d'erreur de syntaxe lorsque l'expression est analysée. J'aimerais aussi savoir s'il y a plus de personnages se comportant comme ça.

GOTO 0
la source
28
@Elyasin Avez-vous copié / collé ou retapé?
user253751
4
Cela fonctionne également dans Visual C #. Lorsque vous collez l'étrange caractère dans l'IDE de Visual Studio, ou lorsque vous terminez l'instruction en tapant ;, l'éditeur a tendance à changer l'étrange "caractère dans un espace normal, mais si vous annulez cette" auto-correction ", vous avez le même comportement . Ce caractère a la même sémantique qu'un espace, même s'il ressemble à un trait d'union ou à un signe moins (dans les polices habituelles).
Jeppe Stig Nielsen du
4
L'inverse peut également se produire. Certaines langues prenant en charge Unicode dans les identificateurs acceptent les caractères Unicode qui ressemblent à des espaces blancs (en d'autres termes, vous ne pouvez pas les voir); il peut même être possible d'avoir des identifiants complètement invisibles.
gnasher729
58
(OT) Parce que 42 est une réponse à tout?
ivan_pozdeev
4
@Thomas le fait que le résultat inattendu était dû à ce caractère Unicode était déjà clair.
GOTO 0

Réponses:

470

Ce caractère est "OGHAM SPACE MARK" , qui est un caractère spatial. Le code est donc équivalent à alert(2+ 40).

J'aimerais aussi savoir s'il y a plus de personnages se comportant comme ça.

Tout caractère Unicode de la classe Zs est un caractère d'espace blanc en JavaScript , mais il ne semble pas y en avoir autant .

Cependant, JavaScript autorise également les caractères Unicode dans les identificateurs , ce qui vous permet d'utiliser des noms de variables intéressants comme ಠ_ಠ.

Felix Kling
la source
3
Box-with-a-hex-code souligne box-with-a-hex-code. De quel personnage s'agit-il?
user253751
12
@immibis La dernière partie de cette réponse est une émoticône disponible sous forme d'image sur disapprovallook.com
Mark S.
3
Notez que non seulement les Zscaractères sont considérés comme des espaces blancs en JavaScript. Il y a plus: github.com/mathiasbynens/regexpu/blob/…
Mathias Bynens
20
Ma réaction quand ಠ_ಠpeut être utilisé comme identifiant dans JS: ಠ_ಠ
Chris Cirefice
2
@ChrisCirefice souligne que le fait d'être traité comme une lettre existe depuis longtemps dans les langages de style C. être traité comme une lettre est du bon sens, car c'est une lettre. Ce serait un bug clair s'il ಠ_ಠne pouvait pas être utilisé comme identifiant.
Jon Hanna
81

Après avoir lu les autres réponses, j'ai écrit un script simple pour trouver tous les caractères Unicode dans la plage U + 0000 – U + FFFF qui se comportent comme des espaces blancs. Il semble qu'il y en ait 26 ou 27 selon le navigateur, avec des désaccords sur U + 0085 et U + FFFE.

Notez que la plupart de ces personnages ressemblent à un espace blanc normal.

GOTO 0
la source
17
U + 0085 "NEL" est défini comme espace blanc par Unicode mais a une longue histoire d'être mal géré. U + FFFE est un non-caractère sans nom ni propriété en dehors de NChar et ne doit pas être considéré comme un espace blanc par quelque chose de raisonnable. Cela dit, mon navigateur n'est pas d'accord avec moi sur les deux points :)
hobbs
4
@hobbs U + FFFE est aussi un \p{Default Ignorable Code Point}, pas seulement un \p{Noncharacter Code Pount}. U + 0085 a toujours été un \p{Whitespace}point de code. Le maléfique est le SÉPARATEUR DE VOIX MONGOLES U + 180E, qui a «récemment» perdu ses \p{Whitespace}biens. Notez qu'il \p{Pattern Whitespace}s'agit d'un ensemble beaucoup plus petit et d'une propriété immuable. Mais \p{Whitespace}non.
tchrist
2
FEFFest la nomenclature et peut être traitée comme un "espace sans interruption de largeur nulle" dans les textes. FFFEest son équivalent swap endian. C'est peut-être la raison pour laquelle certains navigateurs traitent les espaces.
CodesInChaos
ecma-international.org/ecma-262/6.0/#sec-white-space (comme lié à la réponse de Felix King) appelle spécifiquement U + FEFF à être considéré comme un espace blanc dans le code source JS. U + FFFE n'est pas répertorié, mais cela me semble être une erreur d'omission.
zwol
1
@zwol, ce n'est pas une erreur d'omission, car il n'y a pas de caractère U + FFFE. Le traiter comme un espace est un bug. En effet, le traiter comme un caractère valide est un bug dans la plupart des cas. U + 0085 n'est pas un espace blanc selon le spectre JS, mais cette spécification nécessitant un boîtier spécial de U + 0085 pour ne pas être une nouvelle ligne est bizarre et sans doute un bug dans la spécification.
Jon Hanna
56

Il semble que le caractère que vous utilisez est en réalité plus long que le signe moins réel (un trait d'union).

 
-

Le haut est ce que vous utilisez, le bas est ce que le signe moins devrait être. Vous semblez déjà le savoir, alors voyons maintenant pourquoi Javascript fait cela.

Le caractère que vous utilisez est en fait la marque d'espace ogham qui est un caractère d'espace, donc il est fondamentalement interprété comme la même chose qu'un espace, ce qui signifie que votre déclaration ressemble alert(2+ 40)à Javascript.

Il existe d'autres caractères comme celui-ci en Javascript. Vous pouvez voir une liste complète ici sur Wikipedia .


Quelque chose d'intéressant que j'ai remarqué à propos de ce personnage est la façon dont Google Chrome (et d'autres navigateurs possibles) l'interprète dans la barre supérieure de la page.

entrez la description de l'image ici

C'est un bloc avec l' 1680intérieur. Il s'agit en fait du numéro unicode de la marque d'espace ogham. Il semble que ce soit juste ma machine qui le fasse, mais c'est étrange.


J'ai décidé d'essayer cela dans d'autres langues pour voir ce qui se passe et ce sont les résultats que j'ai obtenus.


Langues dans lesquelles il ne fonctionne pas:

Python 2 et 3

>> 2+ 40
  File "<stdin>", line 1
    2+ 40
        ^
SyntaxError: invalid character in identifier

Rubis

>> 2+ 40
NameError: undefined local variable or method ` 40' for main:Object
    from (irb):1
    from /home/michaelpri/.rbenv/versions/2.2.2/bin/irb:11:in `<main>'

Java (à l'intérieur de la mainméthode)

>> System.out.println(2+ 40);
Main.java:3: error: illegal character: \5760
            System.out.println(2+?40);
                                 ^
Main.java:3: error: ';' expected
            System.out.println(2+?40);
                                  ^
Main.java:3: error: illegal start of expression
            System.out.println(2+?40);
                                    ^
3 errors

PHP

>> 2+ 40;
Use of undefined constant  40 - assumed ' 40' :1

C

>> 2+ 40
main.c:1:1: error: expected identifier or '(' before numeric constant
 2+ 40
 ^
main.c:1:1: error: stray '\341' in program
main.c:1:1: error: stray '\232' in program
main.c:1:1: error: stray '\200' in program

exit status 1

Aller

>> 2+ 40
can't load package: package .: 
main.go:1:1: expected 'package', found 'INT' 2
main.go:1:3: illegal character U+1680

exit status 1

Perl 5

>> perl -e'2+ 40'                                                                                                                                   
Unrecognized character \xE1; marked by <-- HERE after 2+<-- HERE near column 3 at -e line 1.

Langues dans lesquelles il fonctionne:

Schème

>> (+ 240)
=> 42

C # (à l'intérieur de la Main()méthode)

Console.WriteLine(2+ 40);

Output: 42

Perl 6

>> ./perl6 -e'say 2+ 40' 
42
michaelpri
la source
34
Ubuntu n'est pas le problème. La police de titre de fenêtre que vous utilisez est.
PSkocik
2
Firefox (Iceweasel) et Google Chrome sur Debian semblent très bien afficher le caractère Unicode, bien que je sois allé trop loin pour assurer la compatibilité Unicode sur mon système. (en fait, la chose la plus utile que j'ai faite était la plus simple sudo apt-get install unicode
:,
@PSkocik Intéressant, j'ai déjà eu des problèmes de police ici, donc c'est probablement probable
michaelpri
51
@PSkocik «Ubuntu n'est pas le problème. La police de titre de fenêtre que vous utilisez est ". … Qui est « Ubuntu ».
user4642212
1
@PSkocik Je l'ai finalement corrigé :) Juste besoin de changer la police de la barre de titre du système.
michaelpri
43

Je suppose que cela doit faire quelque chose avec le fait que pour une raison étrange, il se classe comme un espace blanc:

$ unicode  
U+1680 OGHAM SPACE MARK
UTF-8: e1 9a 80  UTF-16BE: 1680  Decimal: &#5760;( )
Uppercase: U+1680
Category: Zs (Separator, Space)
Bidi: WS (Whitespace)
PSkocik
la source
S'il s'agit d'un copier-coller depuis votre terminal, j'aimerais savoir où vous avez trouvé la commande unicode.
BenjiWiebe
16
C'est du paquet Ubuntu nommé (attendez ...) unicodepar Radovan Garabík. Le dépôt correspondant se trouve sur github.com/garabik/unicode .
PSkocik
OK, merci pour le lien github. AFAICT, ce n'est pas dans les dépôts Fedora.
BenjiWiebe
@PSkocik ' '.codePointAt(0)sur la console donnera 5760. maintenant google 5760 unicode.
Royi Namir
6

J'aimerais aussi savoir s'il y a plus de personnages se comportant comme ça.

Il me semble me souvenir d'avoir lu il y a quelque temps un article sur le remplacement malicieux des points-virgules (U + 003B) dans le code de quelqu'un par U + 037E qui est le point d'interrogation grec.

Ils ont tous deux la même apparence (dans la mesure où je pense que les Grecs eux-mêmes utilisent U + 003B), mais cet article a déclaré que l'autre ne fonctionnerait pas.

Vous trouverez plus d'informations à ce sujet sur Wikipedia: https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

Et une question (fermée) sur l'utilisation de cela comme farce de SO lui-même. Pas là où je l'avais lu AFAIR cependant: JavaScript Prank / Joke

midi
la source