Почему 2+ 40 равно 42?


360

Я был озадачен, когда коллега показал мне эту строку с предупреждением JavaScript 42.

alert(2+ 40);

Быстро выясняется, что то, что выглядит как знак минус, на самом деле является загадочным символом Юникода с явно различной семантикой.

Это заставило меня задуматься, почему этот символ не генерирует синтаксическую ошибку при разборе выражения. Я также хотел бы знать, есть ли еще персонажи, ведущие себя так.


28
@Elyasin Вы копировали / вставляли или перепечатывали?
user253751 20.07.15

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

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

58
(OT) Потому что 42 - это ответ на все вопросы?
ivan_pozdeev

4
@Tommas тот факт, что неожиданный результат был вызван тем символом Unicode, уже был ясен.
ПОЙТИ 0

Ответы:


470

Этот персонаж - «OGHAM SPACE MARK» , это космический персонаж. Таким образом, код эквивалентен alert(2+ 40).

Я также хотел бы знать, есть ли еще персонажи, ведущие себя так.

Любой символ Unicode в классе Zs является символом пробела в JavaScript , но, похоже, их не так много .

Однако JavaScript также позволяет использовать символы Unicode в идентификаторах , что позволяет использовать интересные имена переменных, например ಠ_ಠ.


3
Коробка с шестнадцатеричным кодом Подчеркните коробку с шестнадцатеричным кодом. Каким персонажем это должно быть?
user253751 20.07.15

12
@immibis Последняя часть этого ответа - смайлик, доступный в виде изображения на disapprovallook.com
Марк С.

3
Обратите внимание, что не только Zsсимволы считаются пробелами в JavaScript. Есть еще: github.com/mathiasbynens/regexpu/blob/…
Матиас Биненс

20
Моя реакция, когда ಠ_ಠможно использовать в качестве идентификатора в JS: ಠ_ಠ
Крис Cirefice

2
@ChrisCirefice подчеркивает, что обращение в письме давно существует в языках С-стиля. трактовать как букву - это просто здравый смысл, так как это буква. Это было бы явной ошибкой, если ಠ_ಠбы ее нельзя было использовать в качестве идентификатора.
Джон Ханна

81

Прочитав другие ответы, я написал простой скрипт, чтобы найти все символы Юникода в диапазоне U + 0000 – U + FFFF, которые ведут себя как пробелы. Похоже, их 26 или 27, в зависимости от браузера, с разногласиями по поводу U + 0085 и U + FFFE.

Обратите внимание, что большинство этих символов просто выглядят как обычные пробелы.


17
U + 0085 «NEL» определяется как пробел Unicode, но имеет длительную историю неправильного обращения. U + FFFE - это не символ, не имеющий имени и свойств, кроме NChar, и его не следует рассматривать как пробел с точки зрения чего-либо разумного. Тем не менее, мой браузер не согласен со мной по обоим пунктам :)
Хоббс

4
@hobbs U + FFFE - это тоже \p{Default Ignorable Code Point}не просто \p{Noncharacter Code Pount}. U + 0085 всегда был \p{Whitespace}кодовой точкой. Злой - монгольский разделитель U + 180E, который «недавно» потерял свою \p{Whitespace}собственность. Обратите внимание, что \p{Pattern Whitespace}это гораздо меньший набор и неизменное свойство. Но \p{Whitespace}это не так.
tchrist

2
FEFFявляется спецификацией и может рассматриваться как «пространство без перерывов нулевой ширины» в текстах. FFFEэто эквивалентный регистр Возможно, именно поэтому некоторые браузеры рассматривают это как пробелы.
CodesInChaos

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

1
@zwol, это не ошибка пропуска, потому что нет символа U + FFFE. Относиться к нему как к пробелу - ошибка. Действительно, вообще относиться к нему как к действительному символу - ошибка в большинстве случаев. U + 0085 не является пробелом в соответствии со спектром JS, но та спецификация, которая требует, чтобы специальный регистр U + 0085 не был новой строкой, является странной и, возможно, ошибкой в ​​спецификации.
Джон Ханна

56

Похоже, что используемый вами символ на самом деле длиннее, чем фактический знак минуса (дефис).

 
-

Верхняя часть - это то, что вы используете, нижняя - это то, каким должен быть знак минус. Вы, кажется, уже знаете это, так что теперь давайте посмотрим, почему Javascript делает это.

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

В Javascript есть и другие подобные символы. Вы можете увидеть полный список здесь, в Википедии .


Что-то интересное, что я заметил в этом символе, - это то, как Google Chrome (и, возможно, другие браузеры) интерпретирует его в верхней панели страницы.

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

Это блок с 1680внутренней частью. На самом деле это номер Unicode для знака пробела в 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

Perl 6

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

34
Ubuntu не проблема. Шрифт заголовка окна, который вы используете.
PSkocik

2
Firefox (iceweasel) и Google Chrome в Debian, кажется, прекрасно отображают символы Юникода, хотя я сделал все возможное, чтобы обеспечить совместимость с Юникодом в моей системе. (на самом деле, самая полезная вещь, которую я сделал, была самой простой: sudo apt-get install unicodeхотя только после нескольких часов исследований и неудачных попыток)
sig_seg_v

@PSkocik Интересно, у меня раньше здесь были проблемы со шрифтами, так что, вероятно, вероятно
michaelpri

51
@PSkocik «Ubuntu не проблема. Шрифт заголовка окна, который вы используете: ... который является " Ubuntu ".
user4642212

1
@PSkocik Я наконец-то исправил это :) Просто нужно было изменить системный шрифт в строке заголовка.
Майклпри

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. Теперь Google 5760 Unicode.
Ройи Намир

6

Я также хотел бы знать, есть ли еще персонажи, ведущие себя так.

Кажется, я помню, как читал некоторое время назад статью о хитрой замене точек с запятой (U + 003B) в чьем-то коде на U + 037E, что является греческим вопросительным знаком.

Они оба выглядят одинаково (насколько я думаю, сами греки используют U + 003B), но в этой статье говорится, что другая не будет работать.

Еще немного информации об этом из Википедии здесь: https://en.wikipedia.org/wiki/Question_mark#Greek_question_mark

И (закрытый) вопрос об использовании этого как шутка от самого SO. Не там, где я изначально читал это AFAIR, хотя: JavaScript Prank / Joke

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.