Perché 2+ 40 equivalgono a 42?


360

Ero sconcertato quando un collega mi ha mostrato questa riga di avviso JavaScript 42.

alert(2+ 40);

Si scopre rapidamente che quello che sembra un segno meno è in realtà un arcano personaggio Unicode con una semantica chiaramente diversa.

Questo mi ha lasciato chiedendo perché quel personaggio non produce un errore di sintassi quando si analizza l'espressione. Vorrei anche sapere se ci sono più personaggi che si comportano così.


28
@Elyasin Hai copiato / incollato o digitato nuovamente?
user253751

4
Funziona anche in Visual C #. Quando si incolla lo strano carattere nell'IDE di Visual Studio o quando si completa l'istruzione digitando ;, l'editor tende a cambiare lo strano carattere `` in uno spazio normale, ma se si annulla quella "correzione automatica", si ha lo stesso comportamento . Quel personaggio ha la stessa semantica di uno spazio, anche se sembra un trattino o meno (in caratteri normali).
Jeppe Stig Nielsen,

4
Può succedere anche il contrario. Alcune lingue che supportano Unicode negli identificatori accettano caratteri Unicode che sembrano spazi bianchi (in altre parole, non puoi vederli); potrebbe anche essere possibile avere identificatori completamente invisibili.
gnasher729,

58
(OT) Perché 42 è una risposta a tutto?
ivan_pozdeev,

4
Thomas, il fatto che il risultato inaspettato sia stato causato da quel personaggio Unicode era già chiaro.
GOTO 0

Risposte:


470

Quel personaggio è "OGHAM SPACE MARK" , che è un personaggio spaziale. Quindi il codice è equivalente a alert(2+ 40).

Vorrei anche sapere se ci sono più personaggi che si comportano così.

Qualsiasi personaggio Unicode nella classe Zs è un carattere spazioso in JavaScript , ma non sembrano esserci così tanti .

Tuttavia, JavaScript consente anche caratteri Unicode negli identificatori , che consente di utilizzare nomi di variabili interessanti come ಠ_ಠ.


3
Box-with-a-hex-code underscore box-with-a-hex-code. Quale personaggio dovrebbe essere?
user253751

12
@immibis L'ultima parte di questa risposta è un'emoticon disponibile sotto forma di immagine su disapprovallook.com
Mark S.

3
Nota che non solo i Zscaratteri sono considerati spazi bianchi in JavaScript. Ci sono altri: github.com/mathiasbynens/regexpu/blob/…
Mathias Bynens,

20
La mia reazione quando ಠ_ಠpuò essere usato come identificatore in JS: ಠ_ಠ
Chris Cirefice,

2
Il carattere di sottolineatura di @ChrisCirefice trattato come una lettera è di lunga data nei linguaggi in stile C. essere trattato come una lettera è solo buon senso, dal momento che è una lettera. Sarebbe un chiaro bug se ಠ_ಠnon potesse essere usato come identificatore.
Jon Hanna,

81

Dopo aver letto le altre risposte, ho scritto un semplice script per trovare tutti i caratteri Unicode nell'intervallo U + 0000 – U + FFFF che si comportano come spazi bianchi. A quanto pare, ce ne sono 26 o 27 a seconda del browser, con disaccordi su U + 0085 e U + FFFE.

Nota che la maggior parte di questi personaggi sembra un normale spazio bianco.


17
U + 0085 "NEL" è definito come spazio bianco da Unicode ma ha una lunga storia di maltrattamenti. U + FFFE è un non carattere senza nome e proprietà oltre a NChar e non dovrebbe essere considerato spazio bianco da qualcosa di ragionevole. Detto questo, il mio browser non è d'accordo con me su entrambi i punti :)
hobbs il

4
Anche @hobbs U + FFFE è un \p{Default Ignorable Code Point}, non solo un \p{Noncharacter Code Pount}. U + 0085 è sempre stato un \p{Whitespace}punto di codice. Il malvagio è U + 180E SEPARATORE VOCALE MONGOLIANO, che "recentemente" ha perso la sua \p{Whitespace}proprietà. Si noti che \p{Pattern Whitespace}è un set molto più piccolo e una proprietà immutabile. Ma \p{Whitespace}non lo è.
tchrist,

2
FEFFè la distinta base e può essere trattata come uno "spazio senza interruzioni a larghezza zero" all'interno dei testi. FFFEè equivalente scambiato endian. Forse questo è il motivo per cui alcuni browser trattano come spazi bianchi.
CodesInCos

ecma-international.org/ecma-262/6.0/#sec-white-space (come collegato dalla risposta di Felix King) chiama specificamente U + FEFF per essere considerato spazio bianco nel codice sorgente JS. U + FFFE non è elencato, ma questo mi sembra un errore di omissione.
zwol,

1
@zwol, non è un errore di omissione, perché non c'è il carattere U + FFFE. Trattarlo come uno spazio bianco è un bug. In effetti, trattarlo come un personaggio valido è nella maggior parte dei casi un bug. U + 0085 non è uno spazio bianco secondo lo spettacolo JS, ma quella specifica che richiede un involucro speciale di U + 0085 per non essere una nuova linea è bizzarra e probabilmente un bug nella specifica.
Jon Hanna,

56

Sembra che il personaggio che stai usando sia in realtà più lungo di quello che è il segno meno (un trattino).

 
-

La parte superiore è ciò che stai usando, la parte inferiore è ciò che dovrebbe essere il segno meno. Sembra che tu lo sappia già, quindi ora vediamo perché Javascript lo fa.

Il personaggio che usi è in realtà il segno dello spazio ogham che è un carattere di spazio bianco, quindi è sostanzialmente interpretato come la stessa cosa di uno spazio, il che significa che la tua affermazione assomiglia alert(2+ 40)a Javascript.

Ci sono altri personaggi come questo in Javascript. Puoi vedere un elenco completo qui su Wikipedia .


Qualcosa di interessante che ho notato di questo personaggio è il modo in cui Google Chrome (e possibili altri browser) lo interpretano nella barra superiore della pagina.

inserisci qui la descrizione dell'immagine

È un blocco con 1680dentro di esso. Questo è in realtà il numero unicode per il segno dello spazio ogham. Sembra che sia solo la mia macchina a farlo, ma è una cosa strana.


Ho deciso di provarlo in altre lingue per vedere cosa succede e questi sono i risultati che ho ottenuto.


Lingue in cui non funziona:

Python 2 e 3

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

Rubino

>> 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 (all'interno del mainmetodo)

>> 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

Partire

>> 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.

Lingue in cui funziona:

schema

>> (+ 240)
=> 42

C # (all'interno del Main()metodo)

Console.WriteLine(2+ 40);

Output: 42

Perl 6

>> ./perl6 -e'say 2+ 40' 
42

34
Ubuntu non è il problema. Il carattere del titolo della finestra che stai utilizzando è.
PSkocik,

2
firefox (iceweasel) e google chrome su debian sembrano mostrare bene il carattere Unicode, anche se ho fatto di tutto per garantire la compatibilità Unicode sul mio sistema. (in realtà, la cosa più utile che ho fatto è stata la più semplice sudo apt-get install unicode
:,

@PSkocik Interessante, ho già avuto problemi con i font qui prima, quindi probabilmente è probabile
michaelpri

51
@PSkocik “Ubuntu non è il problema. Il carattere del titolo della finestra che stai utilizzando è. " ... che è " Ubuntu ".
user4642212

1
@PSkocik L'ho finalmente risolto :) Ho solo bisogno di cambiare il carattere della barra del titolo del sistema.
michaelpri,

43

Immagino che abbia a che fare con il fatto che per qualche strana ragione si classifica come spazio bianco:

$ 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)

Se questa è una copia e incolla dal tuo terminale, mi piacerebbe sapere dove hai trovato il comando unicode.
BenjiWiebe,

16
Viene dal pacchetto Ubuntu chiamato (aspetta ...) unicodedi Radovan Garabík. Il repository corrispondente è su github.com/garabik/unicode .
PSkocik,

OK, grazie per il link github. AFAICT, non è nei repository Fedora.
BenjiWiebe,

@PSkocik ' '.codePointAt(0)alla console produrrà 5760. ora google 5760 unicode.
Royi Namir,

6

Vorrei anche sapere se ci sono più personaggi che si comportano così.

Mi sembra di ricordare di aver letto un pezzo qualche tempo fa sulla sostituzione maliziosa di punti e virgola (U + 003B) nel codice di qualcuno con U + 037E che è il punto interrogativo greco.

Entrambi sembrano uguali (nella misura in cui credo che i Greci stessi usino U + 003B) ma questo articolo afferma che l'altro non funzionerebbe.

Alcune ulteriori informazioni su questo da Wikipedia sono qui: https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

E una domanda (chiusa) sull'uso di questo come scherzo da SO stesso. Non dove l'ho letto inizialmente AFAIR però: JavaScript Prank / Joke

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.