Чому 2+ 40 дорівнює 42?


360

Мене було збентежено, коли колега показав мені цей рядок тривоги JavaScript 42.

alert(2+ 40);

Швидко виявляється, що те, що схоже на знак мінус, насправді є прихованим символом Unicode з чітко різною семантикою.

Це змусило мене замислитися, чому цей символ не створює синтаксичної помилки, коли вираз аналізується. Я також хотів би знати, чи є більше персонажів, які так поводяться.


28
@Elyasin Ви копіювали / вставляли чи вводили повторно?
користувач253751

4
Це працює і в Visual C #. Під час вставлення дивного символу в ID Visual Studio або під час заповнення оператора введенням ;редактор прагне змінити дивний символ `` у звичайний простір, але якщо ви скасуєте цю «автоматичну корекцію», ви маєте таку саму поведінку . Цей символ має ту саму семантику, що й пробіл, навіть якщо він схожий на дефіс чи мінус (у звичайних шрифтах).
Джеппе Стіг Нільсен

4
Може статися і навпаки. Деякі мови, що підтримують unicode в ідентифікаторах, приймають символи unicode, які мають вигляд білого простору (іншими словами, ви їх не бачите); можливо, можливо мати абсолютно невидимі ідентифікатори.
gnasher729

58
(ОТ) Тому, що 42 - це відповідь на все?
ivan_pozdeev

4
@Тому, той факт, що несподіваний результат був викликаний тим символом Unicode, вже був зрозумілий.
GOTO 0

Відповіді:


470

Цей персонаж - "OGHAM SPACE MARK" , який є символом пробілу. Тож код еквівалентний alert(2+ 40).

Я також хотів би знати, чи є більше персонажів, які так поводяться.

Будь-який символ Unicode у класі Zs - це білий пробіл у JavaScript , але, здається, їх не так багато .

Однак JavaScript також дозволяє символи Unicode в ідентифікаторах , що дозволяє використовувати цікаві імена змінних, як-от ಠ_ಠ.


3
Підкреслити коробку-з-шістнадцятковим кодом під кутом-з-за-шістнадцятковим кодом Для якого персонажа він мав би бути?
користувач253751

12
@immibis Остання частина цієї відповіді є смайлик доступний у вигляді зображень на disapprovallook.com
Mark S.

3
Зауважте, що не просто Zsсимволи вважаються білим простором у JavaScript. Є ще: github.com/mathiasbynens/regexpu/blob/…
Mathias Bynens

20
Моя реакція, коли ಠ_ಠїї можна використовувати як ідентифікатор у JS: ಠ_ಠ
Chris Cirefice

2
@ChrisCirefice підкреслення, яке розглядається як лист, є давнім мовою в стилі С. трактуватися як лист - це просто здоровий глузд, оскільки це лист. Було б явною помилкою, якби ಠ_ಠїї не можна було використовувати як ідентифікатор.
Джон Ханна

81

Прочитавши інші відповіді, я написав простий скрипт, щоб знайти всі символи Unicode в діапазоні U + 0000 – U + FFFF, які ведуть себе як пробіли. Як здається, їх існує 26 або 27, залежно від браузера, з розбіжностями щодо U + 0085 та U + FFFE.

Зауважте, що більшість із цих символів просто схожі на звичайний пробіл.


17
U + 0085 "NEL" визначається як пробіл Unicode, але має давню історію поводження з ним. U + FFFE - це нехарактерне значення, яке не має імені та ніяких властивостей, окрім NChar, і не повинно вважатися пробілом нічого розумним. Але це означає, що мій браузер не погоджується зі мною в обох пунктах :)
hobbs

4
@hobbs U + FFFE також є \p{Default Ignorable Code Point}, а не просто a \p{Noncharacter Code Pount}. U + 0085 завжди був \p{Whitespace}кодовим пунктом. Злий - це монгольський сепаратист VOWEL U + 180E, який «нещодавно» втратив своє \p{Whitespace}майно. Зауважте, що \p{Pattern Whitespace}це набагато менший набір і незмінне властивість. Але \p{Whitespace}це не так.
tchrist

2
FEFFє BOM і може трактуватися як "нульова ширина простору без перерви" в текстах. FFFEце еквівалентний еквівалентний обмін. Можливо, саме тому деякі браузери ставляться до пробілу.
CodesInChaos

ecma-international.org/ecma-262/6.0/#sec-white-space (як це пов'язано з відповіддю Фелікса Кінга) спеціально закликає U + FEFF вважати пробілом у вихідному коді JS. U + FFFE не вказаний, але це вважає мене помилкою пропуску.
zwol

1
@zwol, це не помилка упущення, оскільки немає символу U + FFFE. Трактувати це як пробіл - це помилка. Дійсно, трактувати його як дійсний персонаж взагалі - це помилка у більшості випадків. U + 0085 - це не пробіл у відповідності зі спектром JS, але те, що вимагає спеціального обшивки U + 0085, щоб не бути новою лінією, є химерним і, можливо, помилкою в специфікації.
Джон Ханна

56

Видається, що персонаж, який ви використовуєте, насправді довший, ніж фактичний знак мінус (дефіс).

 
-

Верх - це те, що ви використовуєте, нижнє - це те, що має бути знак мінус. Ви, здається, це вже знаєте, тому тепер давайте розберемося, чому Javascript робить це.

Символ, який ви використовуєте, є насправді знаком простору ogham, який є символом пробілу, тому він в основному інтерпретується як те саме, що і пробіл, а це означає, що ваше твердження схоже alert(2+ 40)на Javascript.

У Javascript є інші подібні символи. Повний список ви можете побачити тут у Вікіпедії .


Щось цікаве, що я помітив щодо цього персонажа, - це те, як Google Chrome (та можливі інші веб-переглядачі) інтерпретують його у верхній смузі сторінки.

введіть тут опис зображення

Це блок 1680зсередини. Це фактично номер унікоду для пробілу ogham. Здається, це робить саме моя машина, але це дивна річ.


Я вирішив спробувати це на інших мовах, щоб побачити, що відбувається, і це результати, які я отримав.


Мови, на яких це не працює:

Python 2 & 3

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

Рубін

>> 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 (всередині mainметоду)

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

С

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

Іди

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

Мови, якими він працює:

Схема

>> (+ 240)
=> 42

C # (всередині Main()методу)

Console.WriteLine(2+ 40);

Output: 42

Перл 6

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

34
Ubuntu - це не проблема. Використовується шрифт заголовка вікна.
PSkocik

2
firefox (iceweasel) та google chrome на debian, здається, добре відображають char unicode, хоча я намагався забезпечити сумісність unicode в моїй системі. (насправді, найкорисніше, що я зробив, було найпростішим: sudo apt-get install unicodeхоча лише після годинних досліджень і невдалих спроб)
sig_seg_v

@PSkocik Цікаво, що раніше у мене виникли проблеми з шрифтом, так що це, ймовірно,
michaelpri

51
@PSkocik «У Ubuntu не проблема. Використовується шрифт заголовка вікна. " … Що є “ Ubuntu ”.
user4642212

1
@PSkocik я нарешті це виправив :) Просто потрібно змінити шрифт рядка заголовка системи.
michaelpri

43

Я думаю, це має щось робити з тим, що вона з якихось дивних причин класифікується як пробіл:

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

Якщо це копія та вставка з вашого терміналу, я хотів би знати, де ви знайшли команду unicode.
BenjiWiebe

16
Це з пакету Ubuntu, який названий (чекай його ...) unicodeРадована Гарабіка. Відповідне репо є на сайті github.com/garabik/unicode .
PSkocik

Добре, дякую за посилання github. AFAICT, це не в репортажі Fedora.
BenjiWiebe

@PSkocik ' '.codePointAt(0)на консолі дасть 5760. Тепер unicode google 5760.
Рой Намір

6

Я також хотів би знати, чи є більше персонажів, які так поводяться.

Я, здається, пам’ятаю, як читав якийсь час назад про те, що неслухняно замінюють напівколонки (U + 003B) у чийсь код на U + 037E, що є грецьким знаком питання.

Вони обидва виглядають однаково (наскільки я вважаю, що самі греки використовують U + 003B), але ця стаття зазначала, що інша не працює.

Більше інформації про це з Вікіпедії можна знайти тут: https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

І (закрите) питання щодо використання цього в якості сваволі від самого SO. Не там, де я спочатку читав це ПОСЛІ, хоча: Жарти JavaScript / Жарт

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.