Dlaczego 2+ 40 równa się 42?


360

Byłem zaskoczony, gdy kolega pokazał mi tę linię JavaScript z ostrzeżeniem 42.

alert(2+ 40);

Szybko okazuje się, że to, co wygląda jak znak minus, jest w rzeczywistości tajemnym znakiem Unicode o wyraźnie innej semantyce.

To mnie zastanawia, dlaczego ten znak nie powoduje błędu składniowego podczas analizowania wyrażenia. Chciałbym również wiedzieć, czy jest więcej takich postaci.


28
@Elyasin Czy skopiowałeś / wkleiłeś lub przepisałeś?
user253751,

4
Działa to również w Visual C #. Podczas wklejania dziwnej postaci do Visual Studio IDE lub podczas wypełniania instrukcji przez pisanie ;, edytor ma tendencję do zmiany dziwnej `` postaci na normalną przestrzeń, ale jeśli cofniesz tę „automatyczną korektę”, zachowujesz się tak samo . Ten znak ma taką samą semantykę jak spacja, nawet jeśli wygląda jak łącznik lub minus (w zwykłych czcionkach).
Jeppe Stig Nielsen

4
Może się również zdarzyć coś przeciwnego. Niektóre języki obsługujące Unicode w identyfikatorach akceptują znaki Unicode, które wyglądają jak białe znaki (innymi słowy, ich nie widać); może być nawet możliwe posiadanie całkowicie niewidocznych identyfikatorów.
gnasher729,

58
(OT) Ponieważ 42 to odpowiedź na wszystko?
ivan_pozdeev

4
@ Thomas fakt, że nieoczekiwany wynik został spowodowany przez ten znak Unicode, był już jasny.
GOTO 0

Odpowiedzi:


470

Ta postać to „OGHAM SPACE MARK” , która jest postacią spacji. Więc kod jest równoważny z alert(2+ 40).

Chciałbym również wiedzieć, czy jest więcej takich postaci.

Dowolny znak Unicode w klasie Zs jest znakiem JavaScript w białej spacji , ale wydaje się , że nie ma go zbyt wiele .

Jednak JavaScript pozwala również na znaki Unicode w identyfikatorach , co pozwala używać interesujących nazw zmiennych, takich jak ಠ_ಠ.


3
Box-with-a-hex-code podkreśla box-with-a-hex-code. Jaką postacią ma być?
user253751,

12
@immibis Ostatnią częścią tej odpowiedzi jest emotikon dostępny w formie obrazu na stronie disapprovallook.com
Mark S.

3
Pamiętaj, że nie tylko Zsznaki w JavaScript są traktowane jako białe znaki. Jest więcej: github.com/mathiasbynens/regexpu/blob/…
Mathias Bynens

20
Moja reakcja, gdy ಠ_ಠmożna go użyć jako identyfikatora w JS: ಠ_ಠ
Chris Cirefice

2
@ChrisCirefice podkreślenie traktuje się jak literę od dawna w języku Lang w stylu C. traktowanie jak literę jest po prostu zdrowym rozsądkiem, ponieważ jest literą. Byłby to wyraźny błąd, gdyby ಠ_ಠnie mógł zostać użyty jako identyfikator.
Jon Hanna,

81

Po przeczytaniu innych odpowiedzi napisałem prosty skrypt, aby znaleźć wszystkie znaki Unicode z zakresu U + 0000 – U + FFFF, które zachowują się jak białe znaki. Jak się wydaje, jest ich 26 lub 27, w zależności od przeglądarki, z nieporozumieniami na temat U + 0085 i U + FFFE.

Zauważ, że większość z tych postaci wygląda jak zwykła biała spacja.


17
U + 0085 „NEL” jest definiowany przez Unicode jako biały znak, ale ma długą historię niewłaściwej obsługi. U + FFFE jest znakiem innym niż nazwa i bez właściwości oprócz NChar i nie powinien być traktowany jako spacja w jakimkolwiek sensownym przypadku. To powiedziawszy, moja przeglądarka nie zgadza się ze mną w obu punktach :)
hobbs

4
@hobbs U + FFFE to także \p{Default Ignorable Code Point}nie tylko \p{Noncharacter Code Pount}. U + 0085 zawsze był \p{Whitespace}kodem. Zły to U + 180E MONGOLIJSKI SEPARATOR GŁÓWNY, który „niedawno” utracił swoją \p{Whitespace}własność. Zauważ, że \p{Pattern Whitespace}jest to znacznie mniejszy zestaw i niezmienna właściwość. Ale \p{Whitespace}nie jest.
tchrist

2
FEFFjest BOM i może być traktowany jak „przestrzeń zerowa o zerowej szerokości” w tekstach. FFFEjest to ekwiwalent zamiany Endian. Być może dlatego niektóre przeglądarki traktują to jako białe znaki.
CodesInChaos

ecma-international.org/ecma-262/6.0/#sec-white-space (w powiązaniu z odpowiedzią Felixa Kinga) konkretnie wzywa U + FEFF do uznania za spację w kodzie źródłowym JS. U + FFFE nie ma na liście, ale to uderza mnie jako błąd pominięcia.
zwolnienie

1
@zwol, nie jest to błąd pominięcia, ponieważ nie ma znaku U + FFFE. Traktowanie go jako spacji jest błędem. Rzeczywiście, traktowanie go jako prawidłowej postaci jest w większości przypadków błędem. U + 0085 nie jest białą przestrzenią według spektrum JS, ale ta specyfikacja wymagająca specjalnej obudowy U + 0085, aby nie była nową linią, jest dziwna i prawdopodobnie jest błędem w specyfikacji.
Jon Hanna,

56

Wygląda na to, że znak, którego używasz, jest w rzeczywistości dłuższy niż rzeczywisty znak minus (łącznik).


-

Góra jest tym, czego używasz, dół to znak minus. Wydaje się, że już to wiesz, więc teraz zobaczmy, dlaczego JavaScript to robi.

Znak, którego używasz, jest tak naprawdę znakiem spacji ogham, który jest znakiem spacji, więc jest zasadniczo interpretowany jako to samo co spacja, co oznacza, że ​​twoje wyrażenie wygląda jak alert(2+ 40)JavaScript.

Istnieją inne takie znaki w Javascript. Możesz zobaczyć pełną listę tutaj na Wikipedii .


Coś interesującego, co zauważyłem w tej postaci, to sposób, w jaki Google Chrome (i możliwe inne przeglądarki) interpretuje ją na górnym pasku strony.

wprowadź opis zdjęcia tutaj

To jest blok z jego 1680wnętrzem. Jest to w rzeczywistości numer Unicode znaku spacji oghama. Wygląda na to, że robi to tylko moja maszyna, ale to dziwna rzecz.


Postanowiłem wypróbować to w innych językach, aby zobaczyć, co się stanie i takie są wyniki, które uzyskałem.


Języki, w których nie działa:

Python 2 i 3

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

Rubin

>> 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 (wewnątrz mainmetody)

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

do

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

Udać się

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

Języki, w których działa:

Schemat

>> (+ 240)
=> 42

C # (wewnątrz Main()metody)

Console.WriteLine(2+ 40);

Output: 42

Perl 6

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

34
Ubuntu nie jest problemem. Używana czcionka tytułu okna to.
PSkocik,

2
firefox (iceweasel) i Google Chrome na Debianie wydają się wyświetlać znak Unicode w porządku, chociaż dołożyłem wszelkich starań, aby zapewnić kompatybilność z Unicode w moim systemie. (właściwie najbardziej użyteczna rzecz, którą zrobiłem, była najprostsza: sudo apt-get install unicodechociaż dopiero po wielu godzinach badań i nieudanych próbach)
sig_seg_v

@PSkocik Interesujące, miałem wcześniej problemy z czcionkami, więc prawdopodobnie jest to prawdopodobne
michaelpri

51
@PSkocik „Ubuntu nie jest problemem. Używana czcionka tytułu okna to. ” CzyliUbuntu ”.
user4642212,

1
@PSkocik W końcu to naprawiłem :) Potrzebowałem tylko zmienić czcionkę systemową paska tytułu.
michaelpri,

43

Wydaje mi się, że ma to coś wspólnego z tym, że z jakiegoś dziwnego powodu klasyfikuje się go jako biały znak:

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

Jeśli jest to kopia i wklej z terminala, chciałbym wiedzieć, gdzie znalazłeś polecenie unicode.
BenjiWiebe,

16
Pochodzi z pakietu Ubuntu o nazwie (poczekaj na to ...) unicodeautorstwa Radovana Garabíka. Odpowiednie repo znajduje się na stronie github.com/garabik/unicode .
PSkocik,

OK, dzięki za link github. AFAICT, nie ma go w repozytoriach Fedory.
BenjiWiebe,

@PSkocik ' '.codePointAt(0)na konsoli da 5760. teraz google 5760 Unicode.
Royi Namir,

6

Chciałbym również wiedzieć, czy jest więcej takich postaci.

Wydaje mi się, że pamiętam jakiś czas temu o złośliwym zastępowaniu średników (U + 003B) w czyimś kodzie U + 037E, który jest greckim znakiem zapytania.

Oba wyglądają tak samo (do tego stopnia, że ​​uważam, że sami Grecy używają U + 003B), ale w tym artykule stwierdzono, że drugi nie zadziała.

Więcej informacji na ten temat z Wikipedii znajduje się tutaj: https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

I (zamknięte) pytanie dotyczące używania tego jako dowcipu z samego SO. Ale nie tam, gdzie go pierwotnie przeczytałem AFAIR: JavaScript Prank / Joke

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.