Чому дозволено виконання коду Java в коментарях з певними символами Unicode?


1356

Наступний код дає вихід "Hello World!" (ні, справді, спробуйте).

public static void main(String... args) {

   // The comment below is not a typo.
   // \u000d System.out.println("Hello World!");
}

Причиною цього є те, що компілятор Java розбирає символ Unicode \u000dяк новий рядок і перетворюється на:

public static void main(String... args) {

   // The comment below is not a typo.
   //
   System.out.println("Hello World!");
}

Таким чином, коментар "виконується".

Оскільки це можна використовувати для "приховування" зловмисного коду або того, що може придумати злий програміст, чому це дозволено в коментарях ?

Чому це дозволено специфікацією Java?


44
"Чому це дозволено" мені здається занадто обґрунтованим думкою. Мовні дизайнери прийняли рішення, що ще потрібно знати? Якщо ви не знайдете заяву особи, яка приймає це рішення, ми можемо лише міркувати.
Інго Бюрк

194
Цікавим є принаймні те , що IDE ОП, очевидно, помиляється та показує неправильне підсвічування,
dhke

14
Можливо , пов'язані з : stackoverflow.com/questions/4448180 / ...
dhke

47
@Tobb Але дизайнери Java відвідують SO, тому відповіді можна отримати по одному з них. Також у них можуть існувати ресурси, які вже відповідають на це питання.
Пшемо

41
Проста відповідь полягає в тому, що код зовсім не в коментарі за правилами мови, тому питання неправильно сформовано.
користувач207421

Відповіді:


741

Розшифровка Unicode відбувається перед будь-яким іншим лексичним перекладом. Ключова перевага цього полягає в тому, що це робить тривіальним перехід назад і назад між ASCII та будь-яким іншим кодуванням. Вам навіть не потрібно розбиратися, де починаються і закінчуються коментарі!

Як зазначено в розділі 3.3 JLS, це дозволяє будь-якому інструменту на основі ASCII обробляти вихідні файли:

[...] Мова програмування Java визначає стандартний спосіб перетворення програми, написаної в Unicode, в ASCII, що змінює програму у форму, яку можна обробити інструментами на основі ASCII. [...]

Це дає фундаментальну гарантію незалежності платформи (незалежність підтримуваних наборів символів), що завжди було ключовою метою платформи Java.

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

На цю тему є багато ґутів, і Java Puzzlers Джошуа Блоха та Ніла Гафтера включили наступний варіант:

Це легальна програма Java? Якщо так, що це друкує?

\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020
\u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079
\u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020
\u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063
\u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028
\u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020
\u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b
\u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074
\u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020
\u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b
\u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d

(Ця програма виявляється простою програмою "Hello World".)

У рішенні головоломки вони вказують на наступне:

Більш серйозно, ця головоломка служить підсиленням уроків попередніх трьох: Уникнення Unicode є важливим, коли вам потрібно вставити символи, які не можуть бути представлені іншим чином у вашій програмі. Уникайте їх у всіх інших випадках.


Джерело: Java: Виконання коду в коментарях ?!


84
Коротше кажучи, Java навмисно дозволяє це: "помилка" в IDE ОП?
Вірсавія

60
@Bathsheba: Це більше в головах людей. Люди не намагаються зрозуміти, як працює розбір Java, тому ІДЕ іноді відображає код неправильно. У наведеному вище прикладі коментар повинен закінчуватися, \u000dа частина після нього повинна мати виділення коду.
Аарон Дігулла

62
Ще одна поширена помилка - вставити шляхи Windows у такий код, // C:\user\...який призводить до помилки компіляції, оскільки \userце не є дійсною послідовністю виходу Unicode.
Аарон Дігулла

50
У затемненні Кодекс після \u000dвиділяється частково. Після натискання клавіші Ctrl + Shift + F персонаж замінюється новим рядком, а решта загортається
bluelDe

20
@TheLostMind Якщо я правильно зрозумів відповідь, ви повинні мати змогу відтворити це також за допомогою блокових коментарів. \u002A/слід закінчити коментар.
Taemyr

141

Оскільки цього ще не вдалося вирішити, ось пояснення, чому переклад Unicode виходить, відбувається перед будь-якою іншою обробкою вихідного коду:

Ідея його полягала в тому, що вона дозволяє перекладати вихідний код Java без втрат між різними кодуваннями символів. Сьогодні існує широка підтримка Unicode, і це не виглядає проблемою, але тоді розробнику із західної країни було непросто отримати від його азіатського колеги вихідний код, що містить азіатські символи, внести деякі зміни ( включаючи його складання та тестування) та відправлення результату назад, все, не пошкоджуючи щось.

Отже, вихідний код Java може бути записаний у будь-якому кодуванні та дозволяє отримати широкий спектр символів у межах ідентифікаторів, символів та Stringлітералів та коментарів. Потім, щоб перенести це без втрат, всі символи, не підтримувані цільовим кодуванням, замінюються на їх уникнення Unicode.

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

Це причина ще однієї дивної особливості, про яку навіть не згадували: \uuuuuuxxxxсинтаксис:

Коли інструмент перекладу uпереходить до символів і стикається з послідовністю, яка вже є уникнутою послідовністю, він повинен вставити додатковий у послідовність, перетворюючись \ucafeна \uucafe. Значення не змінюється, але при перетворенні в інший бік інструмент повинен просто видалити один uі замінити лише послідовності, що містять одиницю u, їх символами Unicode. Таким чином, навіть втечі Unicode зберігаються в первісному вигляді при перетворенні назад і назад. Я здогадуюсь, ніхто ніколи не використовував цю функцію ...


1
Цікаво, native2asciiщо, схоже, не використовується \uu...xxxxсинтаксис,
ninjalj

5
Так, native2asciiмав на меті допомогти підготувати пакети ресурсів, перетворивши їх на ізо-латиніну-1, як Properties.loadбуло встановлено лише для читання латинської мови-1. І там правила різні, немає \uuu…синтаксису і немає стадії ранньої обробки. У файлах властивостей property=multi\u000alineдійсно те саме, що і у property=multi\nline. (Суперечить фразі "використання уникнення Unicode, як визначено в розділі 3.3" Специфікації мови Java ") документації.
Holger

10
Зауважте, що цієї мети дизайну можна було досягти без жодної бородавки; найпростішим способом було б заборонити \uвтечі генерувати символи в діапазоні U + 0000–007F. (Усі подібні символи можуть бути представлені на національному рівні всіма національними кодуваннями, які були актуальними у 90-х роках. Ну, може бути, крім деяких контрольних символів, але вони вам не потрібні, щоб писати Java.)
zwol

3
@zwol: добре, якщо ви виключаєте контрольні символи, які все одно заборонені у вихідному коді Java, ви маєте рацію. Тим не менш, це означатиме ускладнення правил. І сьогодні вже пізно обговорювати рішення ...
Хольгер

ах проблема збереження документа у utf8, а не в латинській мові чи щось інше. Усі мої бази даних також були порушені через цю західну нісенітницю
Девід Jun Вонг,

106

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

У вихідному коді Java \ u000d у всіх відношеннях еквівалентний символу CR ASCII. Це лінія, що закінчується, простою і простою, де б вона не відбулася. Форматування у запитанні вводить в оману, чому ця послідовність символів насправді синтаксично відповідає:

public static void main(String... args) {
   // The comment below is no typo. 
   // 
 System.out.println("Hello World!");
}

ІМХО, найправильніша відповідь - це: код виконується, оскільки він не знаходиться в коментарі; це на наступному рядку. "Виконання коду в коментарях" заборонено на Java, як і ви очікували.

Значна плутанина пов'язана з тим, що підсвічувачі синтаксису та IDE недостатньо складні, щоб врахувати цю ситуацію. Вони або взагалі не обробляють уникнення унікоду, або роблять це після розбору коду замість цього, як раніше javac.


6
Я згоден, це не java "дизайнерська помилка", але це помилка IDE.
bvdb

3
Питання швидше полягає в тому, чому код, схожий на коментар тому, хто не знайомий з цим особливим аспектом мови і, можливо, без посилання на виділення синтаксису, насправді не є коментарем. Заперечення, виходячи з передумови питання недійсного, є неохайним.
Філ

@Phil: коментар виглядає лише як коментар, коли його переглядають певні інструменти, інші показують це інакше.
jmoreno

1
@jmoreno для читання коду не повинно бути нічого, крім текстового редактора. Принаймні, це порушує принцип найменшого здивування, а саме те, що // коментарі до стилю продовжуються до наступного символу \ n - не до будь-якої іншої послідовності, яка в кінцевому підсумку буде замінена на \ n. Від коментарів ніколи не очікується нічого іншого, крім позбавленого. Поганий препроцесор.
Філ

69

\u000dВтеча закінчується коментар , тому що \uпагони поступово перетворюються на відповідні символи Unicode , перш ніж програма лексеми. Ви можете однаково використовувати \u0057\u0057замість того, //щоб розпочати коментар.

Це помилка у вашій IDE, яка повинна виділити синтаксис рядком, щоб зрозуміти, що \u000dкоментар закінчується.

Це також помилка дизайну в мові. Зараз це неможливо виправити, оскільки це порушить програми, які залежать від цього. \uEscape слід або перетворити на відповідний символ Unicode компілятором лише в контекстах, де це "має сенс" (рядкові літерали та ідентифікатори, і, мабуть, ніде більше), або їм було б заборонено генерувати символи в діапазоні U + 0000–007F або те й інше. Будь-яка з цих семантик не завадила б коментарю припинитися \u000dвтечею, не втручаючись у випадки, коли \uвтечі є корисними - зауважте, що це включає використання \uескадрів у коментарях як спосіб кодування коментарів у не латинській письмі, оскільки текстовий редактор міг би розширити уявлення про те, куди\uвтечі важливіші, ніж робить компілятор. (Я не знаю жодного редактора або IDE, який відображатиме \uвхідні файли як відповідні символи в будь-якому контексті.)

Існує аналогічна помилка проектування в сімействі C 1, де обробляється зворотний косий рядок перед визначенням меж коментарів, наприклад,

// this is a comment \
   this is still in the comment!

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

1 Для педантів: Я усвідомлюю, що цей аспект С був на 100% навмисним, з обґрунтуванням - я цього не вигадую - що це дозволить вам механічно примусово застосовувати код з довільно довгими рядками на перфокарти. Це все ж було неправильним дизайнерським рішенням.


17
Я б не пішов так далеко, щоб сказати, що це помилка дизайну . Я міг би погодитися з вами, що це був поганий вибір дизайну або вибір із сумними наслідками, але я все одно вважаю, що він працює так, як планували дизайнери: Це дозволяє використовувати будь-який символ Unicode в будь-якому місці файлу, зберігаючи кодування ASCII файлу.
aioobe

12
Зважаючи на це, я вважаю, що вибір етапу обробки для нього \uбув менш абсурдним, ніж рішення слідувати ведучому С у використанні провідних нулів для восьмеричного позначення. Хоча восьмеричні позначення іноді корисні, я ще не чую, щоб хтось сформулював аргумент, чому провідний нуль - це хороший спосіб його позначення.
supercat

3
@supercat Люди, які кинули цю функцію на C89, узагальнювали поведінку оригінального препроцесора K&R, а не розробляли функцію з нуля. Я сумніваюся, що вони були знайомі з найкращими методами перфокарт, і я також сумніваюся, що ця функція коли-небудь використовувалася за вказаним призначенням, за винятком, можливо, однієї або двох вправ на обчислення.
zwol

8
@supercat У мене не було б проблеми з Java \uяк передтокенізаційної трансформації, якби було заборонено створювати символи в діапазоні U + 0000..U + 007F. Саме поєднання "це працює скрізь" і "цей псевдонім символів ASCII з синтаксичним значенням" демонструє його від незручного до неправильного виходу.
zwol

4
На ваш «для педантів»: Звичайно , в той час однорядковий коментар не існує . А оскільки у C є термінатор висловлювань, який не є новим рядком, він би в основному використовувався для довгих рядків, за винятком того, що, наскільки я можу визначити, "рядкове буквальне з'єднання" було там від K&R. //
Марк Херд

22

Це був навмисний вибір дизайну, який іде повністю назад до оригінального дизайну Java.

Тим людям, які запитують "хто хоче, щоб у коментарях Unicode врятувався?", Я припускаю, що це люди, чия рідна мова використовує набір символів латинської мови. Іншими словами, оригінальному дизайну Java властиво те, що люди можуть використовувати довільні символи Unicode, де б вони не були легальними в програмі Java, як правило, в коментарях та рядках.

Можливо, це недолік у програмах (наприклад, IDE), які використовуються для перегляду вихідного тексту, що такі програми не можуть інтерпретувати уникнення Unicode та відображення відповідного гліфа.


8
Сьогодні ми використовуємо UTF-8 для нашого вихідного коду і можемо використовувати символи Unicode безпосередньо, не потребуючи втечі.
Paŭlo Ebermann

21

Я погоджуюся з @zwol, що це помилка дизайну; але я ще більше критикуюсь до цього.

\uвтеча корисна в рядкових і знакових літералах; і це єдине місце, де воно повинно існувати. З нею слід поводитися так само, як і з іншими втечами \n; і "\u000A" має означати точно "\n".

Немає сенсу брати \uxxxxкоментарі - ніхто не може цього прочитати.

Так само немає сенсу використовувати \uxxxxв іншій частині програми. Єдиним винятком є, мабуть, публічні API, які примушують містити деякі символи, що не мають права - що це ми бачили останній раз?

Свої причини дизайнери мали в 1995 році, але через 20 років це виявляється неправильним вибором.

(запитання до читачів - чому це питання продовжує отримувати нові голоси? Це питання пов’язане звідкись популярним?)


5
Я думаю, ви не зависаєте, де в API використовуються символи, що не належать до ASCII. Є люди, які використовують це (не я), наприклад, в азіатських країнах. І коли ви використовуєте символи, що не належать до ASCII, в ідентифікаторах, забороняти їх у коментарях до документації мало сенсу. Тим не менш, дозволити їм всередині маркера і дозволити їм змінити значення або мету лексеми - це різні речі.
Холгер

15
вони можуть використовувати правильне кодування файлів. навіщо писати, int \u5431коли можна робитиint 整
ZhongYu

3
Що ви будете робити, коли вам доведеться компілювати код проти їх API і не можете використовувати належне кодування (припустимо, що UTF-8в 1995 році не було широкої підтримки). Вам просто потрібно зателефонувати в один метод і не хочете встановлювати пакет підтримки азіатської мови вашої операційної системи (пам’ятайте, дев'яностих років) для цього єдиного методу…
Holger

5
Зараз набагато зрозуміліше, ніж 1995 рік, це те, що ви краще знаєте англійську мову, якщо хочете програмувати. Програмування - це міжнародна взаємодія, і майже всі ресурси англійською мовою.
ZhongYu

8
Я не думаю, що це змінилося. Документація Java також була більш англійською мовою. На деякий час зберігався переклад з японської мови, але збереження двох мов насправді не підтримує ідею збереження його для всіх локальних верств світу (це досить спростувало). А до цього в жодному разі не було жодної основної мови з підтримкою Unicode в ідентифікаторах. Тож я б здогадався, хтось подумав, що наступна велика річ - локалізований вихідний код. Я б сказала з вдячністю , це не злетіло.
Холгер

11

Ті, хто може відповісти, чому втіки Unicode були реалізовані такими, як вони, - це люди, які написали специфікацію.

Імовірною причиною цього є те, що було бажання дозволити всій BMP як можливі символи вихідного коду Java. Це створює проблему:

  • Ви хочете мати можливість використовувати будь-який символ BMP.
  • Ви хочете мати можливість вводити будь-яку схему BMP досить просто. Спосіб зробити це за допомогою втечі Unicode.
  • Ви хочете, щоб лексична специфікація була простою для людей для читання та запису, а також досить простою для реалізації.

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

Найпростіший вихід - зробити лексінг у два етапи: спочатку шукайте та замінюйте всі втечі Unicode символом, який він представляє, а потім аналізуйте отриманий документ так, як ніби уникнення Unicode не існують.

Проблема полягає в тому, що це легко вказати, тому він робить специфікацію простішою і її легко реалізувати.

Мінус - ну, ваш приклад.


2
Або обмежте використання \ uxxxx ідентифікаторами, рядковими літералами та константами символів. Це те, що робить C11.
ninjalj

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