Почему этот код, написанный задом наперед, печатает " Hello World!"

вот код, который я нашел в Интернете:

class M‮{public static void main(String[]a‭){System.out.print(new char[]
{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}    

этот код выводит Hello World! на экран; вы можете видеть, как он работает здесь. Я ясно вижу public static void main написано, но задом наперед. Как работает этот код? Как это вообще компилируется?

Edit: Я пробовал этот код в IntellIJ, и он отлично работает. Однако по какой-то причине он не работает в notepad++ вместе с cmd. Я до сих пор не нашел решение, так что если кто делает, комментарий ниже.

4 ответов


здесь есть невидимые символы, которые изменяют способ отображения кода. В Intellij их можно найти, скопировав код в пустую строку (""), который заменяет их на Unicode escapes, удаляя их эффекты и раскрывая порядок, который видит компилятор.

вот результат этой copy-paste:

"class M\u202E{public static void main(String[]a\u202D){System.out.print(new char[]\n"+
        "{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}   "

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

Примечание \u202E символ, который является переопределением справа налево, начиная блок, где все символы вынуждены отображаться справа налево, и \u202D, который является переопределением слева направо, начиная вложенный блок, где все символы принудительно в порядке слева направо, переопределяя первое переопределение.

Ergo, когда он отображает исходный код,class M отображается нормально, но \u202E отменяет порядок отображения всего из там \u202D, который снова все меняет. (Формально, все от \u202D к линии Терминатор получает обратный дважды, один раз из-за \u202D и один раз с остальной частью текста отменяется из-за \u202E, поэтому этот текст отображается в середине строки вместо конца.) Направленность следующей строки обрабатывается независимо от первой из-за Терминатора линии, поэтому {'H','e','l','l','o',' ','W','o','r','l','d','!'});}} отображается нормально.

для полного (чрезвычайно сложного, десятки страниц) двунаправленный алгоритм Unicode, см. Стандартное Приложение Unicode #9.


Это выглядит по-другому из-за Двунаправленный Алгоритм Unicode. Есть два невидимых символа RLO и LRO, которые двунаправленный алгоритм Unicode использует для изменения внешний вид символов, вложенных между этими двумя метасимволами.

в результате визуально они смотрят в обратном порядке, но фактические символы в не восстанавливаются. Вы можете проанализировать результаты здесь. Компилятор Java будет игнорировать RLO и LRO и рассматривать их как пробелы, поэтому код компилируется.

Примечание 1: Этот алгоритм используется текстовыми редакторами и браузерами для визуального отображения символов как LTR-символов (на английском языке), так и RTL-символов (например Арабский, иврит) вместе в то же время-отсюда "Би" - направленный. Вы можете узнать больше о Двунаправленном алгоритме в Юникоде сайт.
примечание 2: Точное поведение LRO и RLO определяется в 2.2 of алгоритм.


Символ U+202E отражает код справа налево, это очень умно, хотя. Скрыто начиная с M,

"class M\u202E{..."

как я нашел магия за это?

Ну, сначала, когда я увидел вопрос, я жесткий, "это своего рода шутка, чтобы потерять кого-то еще время", но затем я открыл свою IDE ("IntelliJ"), создать класс и мимо кода... и он составлен!!! Я присмотрелся и увидел, что "публичная статическая пустота" была обратной, поэтому я пошел туда с курсором,и стереть несколько символов... И что происходит? символы начали стирать назад, Итак, я подумал МММ.... редкий... Я должен его выполнить... Поэтому я приступаю к выполнению программы, но сначала мне нужно сохранить... и это было когда я нашел его!. Я не смог сохранить файл, потому что моя IDE сказала, что для некоторых символов есть другая кодировка,и мне где он был?!--13-->, поэтому я начинаю исследование в Google для специальных символов, которые могли бы выполнить эту работу, и все:)

о

двунаправленный алгоритм Unicode и U+202E участвует, кратко объяснить:

стандарт Юникода предписывает порядок представления памяти, известный как логический порядок. Когда текст представлен горизонтальными линиями, большинство скриптов отображает символы слева направо. Однако, существует несколько сценариев (например, арабский или иврит), в которых естественный порядок горизонтального текста отображается справа налево. Если весь текст имеет равномерное горизонтальное направление, то порядок отображения текста однозначен.

однако, поскольку эти скрипты справа налево используют цифры, которые написаны слева направо, текст на самом деле двунаправленный: смесь текста справа налево и слева направо. В дополнение к цифрам, врезанным словам от английского языка и другие сценарии также пишутся слева направо, также создавая двунаправленный текст. Без четкой спецификации могут возникнуть неоднозначности при определении порядка отображаемых символов, когда горизонтальное направление текста неоднородно.

В этом приложении описывается алгоритм, используемый для определения направленности двунаправленного текста Юникода. Алгоритм расширяет неявную модель, используемую в настоящее время рядом существующих реализаций, и добавляет явную форматирование символов для особых обстоятельств. В большинстве случаев нет необходимости включать дополнительную информацию в текст для получения правильного порядка отображения.

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

зачем создавать какой-то алгоритм, как этой?

алгоритм bidi может отображать последовательность арабского или иврита персонажи один за другим справа налево.

P. S.: Я знаю, что это не лучший ответ, но это было весело, чтобы взломать проблема первая: P


Глава 3 спецификации языка предоставляет объяснение, подробно описывая, как лексический перевод выполняется для программы Java. Что наиболее важно для вопроса:

программы написаны в Юникоде (§3.1), но лексические переводы предоставляются (§3.2), так что Unicode escapes (§3.3) можно использовать для включения любого символа Unicode, используя только символы ASCII.

таким образом, программа написана в Символы Unicode, и автор может избежать их с помощью \uxxxx в случае, если кодировка файла не поддерживает Юникод, в этом случае он переводится на соответствующий символ. Один из символов Unicode в данном случае составляет \u202E. Он визуально не отображается в фрагменте, но при попытке переключения кодировки браузера могут появиться скрытые символы.

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

class M\u202E{

это означает, что идентификатор класса M\u202E. The спецификация рассматривает это как действительный идентификатор:

Identifier:
    IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
IdentifierChars:
    JavaLetter {JavaLetterOrDigit}

"Java letter-or-digit" - это символ, для которого метод Character.isJavaIdentifierPart(int) возвращает true.