2016-03-09 2 views
5

Несколько дней назад, я был задан вопрос о выходе этой программы:Unicode поведение побег в программах Java

public static void main(String[] args) { 
    // \u0022 is the Unicode escape for double quote (") 
    System.out.println("a\u0022.length() + \u0022b".length()); 
} 

Моя первая мысль была эта программа должна напечатать длину a\u0022.length() + \u0022b, которая 16, но удивительно, что напечатано 2 , Я знаю, что \u0022 - это юникод для ", но я думал, что это " будет экранировано и представляет только один номер " буквально, без особого значения. И в самом деле, Java каким-то образом проанализирован эту строку следующим образом:

System.out.println("a".length() + "b".length()); 

Я не могу обернуть мою голову вокруг этого странного поведения, почему Unicode ускользает не ведут себя как обычные управляющие последовательности?

Обновление По-видимому, это был один из мозговых дразнилок из книги Java Puzzlers: Traps, Pitfalls, and Corner Cases, написанной Джошуа Блохом и Нилом Гафтером. Более конкретно, вопрос был связан с Puzzle 14: Escape Rout.

+4

http://www.javajee.com/unicode-escapes-in-java – ShrtTth

ответ

6

Почему Unicode сбегает не ведут себя как нормальные управляющие последовательности?

В принципе, они обрабатываются в другом месте при чтении ввода - в лексинге, а не в синтаксическом анализе, если у меня есть правильная терминология. Они не являются escape-последовательностями в символьных литералах или строковых литералах, это escape-последовательности для всего исходного файла. Любой символ, который не является частью escape-последовательности Unicode, может быть заменен на escape-последовательность Unicode. Таким образом, вы можете полностью писать программы в ASCII, которые на самом деле имеют имена переменных, методов и классов, которые не являются ASCII ...

По сути, я считаю, что это была ошибка дизайна на Java, поскольку это может вызвать некоторые очень странные эффекты (например, если у вас есть escape-последовательность для разрыва строки в комментарии // ...), но это то, что это ...

Это подробно описано в section 3.3 of the JLS:

компилятор для языка программирования Java («Java компилятор») первым распознает Unicode сбегает в своем входе, переводя символы ASCII \ и следуют четыре шестнадцатеричных цифр UTF-16 (§3.1) для указанного шестнадцатеричного значения и передачи всех остальных символов без изменений. Представление дополнительных символов требует двух последовательных Unicode-экранов. Этот шаг перевода приводит к последовательности входных символов Unicode.

...

Язык программирования Java определяет стандартный способ преобразования программы, написанной в Unicode в ASCII, который изменяет программу в форме, которая может быть обработана с помощью ASCII-инструментов. Преобразование включает в себя преобразование всех экранов Unicode в исходный текст программы в ASCII путем добавления дополнительного u - например, \ uxxxx становится \ uuxxxx - при одновременном преобразовании не-ASCII-символов в исходном тексте в escape-последовательности Unicode, содержащих один u каждый ,

Эта преобразованная версия одинаково приемлема для компилятора Java и представляет собой ту же самую программу. Точный источник Unicode может быть позже восстановлен из этой формы ASCII, преобразовывая каждую escape-последовательность, где несколько u присутствуют в последовательности символов Unicode с одним меньшим u, одновременно конвертируя каждую escape-последовательность с одним u в соответствующий один символ Unicode.

7

Перед компилятор на самом деле переводит источник в байткод, лексический фаза перевода повернет заявление:

System.out.println("a\u0022.length() + \u0022b".length()); 

в:

System.out.println("a".length() + "b".length()); 

Следовательно, результат 2.

также см. this section about lexical translation с указанием языка:

Исходный поток символов Unicode, переводится в последовательность лексем, используя следующие три лексические шаги по переводу, которые применяются в свою очередь:

  1. Перевод Unicode сбегает (§3.3) в сыром потоке Юникод символов к соответствующему символу Юникода. Выделение Unicode формы \ uxxxx, где xxxx является шестнадцатеричным значением, представляет собой единицу кода UTF-16, кодировка которой равна xxxx. Этот шаг перевода позволяет выражать любую программу с использованием только символов ASCII.
+2

Важно отметить, что это * длинный * перед эмиссией байтового кода - это до того, как будет выполнен любой другой синтаксический анализ. –

+1

@JonSkeet Спасибо за информацию. Возможно, я должен был использовать термин lexer вместо parser :) – manouti

+0

@ AR.3 Спасибо за ваш ответ, оба ответа велики, и у меня сложнее всего выбрать один, как принято. Во всяком случае, я собираюсь принять ответ Джона, спасибо снова. Хотел бы я согласиться и с тобой :) –

0

Это просто смешно, что следующие работы (взяты из ссылки)

System.out.println("a\".length() + \"b".length()); 

но следующие выдает ошибку компиляции

System.out.println("a\\\u0022.length() + \\\u0022b".length()); 

На втором, компилятор должен уменьшить \ и ", соедините их как \", но он попробовал и не скомпилировал (" все еще закрывает стринги г).

Смежные вопросы