为什么这段代码,向后写,打印 "Hello World!"
Why does this code, written backwards, print "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!
打印到屏幕上;你可以看到它 运行 here。我可以清楚地看到写着 public static void main
,但是它是倒着的。这段代码是如何工作的?这甚至如何编译?
编辑: 我在 IntellIJ 中试过这段代码,它工作正常。但是,由于某种原因,它在 notepad++ 和 cmd 中不起作用。我还没有找到解决办法,所以如果有人找到了,请在下面评论。
字符U+202E
从右到左镜像代码,虽然很聪明。隐藏在M开头,
"class M\u202E{..."
How did I found the magic behind this?
好吧,一开始我看到这个问题的时候我很难,"it's a kind of joke, to lose somebody else time",但是后来,我打开了我的IDE("IntelliJ"),创建了一个class,并通过代码... 并编译!!!
所以,我仔细看了一下,发现 "public static void" 是向后的,所以我用光标去那里, 并删除了几个字符 ...
会发生什么? 字符开始向后擦除,所以,我想嗯....很少见...我必须执行它...所以我继续执行程序,但首先我需要保存它...那是我找到它的时候!。我无法保存文件,因为我的 IDE 说某些字符有不同的编码, 并指出它在哪里 ,所以我开始研究 Google 用于可以完成这项工作的特殊字符,仅此而已:)
A little about
Unicode 双向算法,U+202E
涉及,简要说明 explain:
The Unicode Standard prescribes a memory representation order known as logical order. When text is presented in horizontal lines, most scripts display characters from left to right. However, there are several scripts (such as Arabic or Hebrew) where the natural ordering of horizontal text in display is from right to left. If all of the text has a uniform horizontal direction, then the ordering of the display text is unambiguous.
However, because these right-to-left scripts use digits that are written from left to right, the text is actually bi-directional: a mixture of right-to-left and left-to-right text. In addition to digits, embedded words from English and other scripts are also written from left to right, also producing bidirectional text. Without a clear specification, ambiguities can arise in determining the ordering of the displayed characters when the horizontal direction of the text is not uniform.
This annex describes the algorithm used to determine the directionality for bidirectional Unicode text. The algorithm extends the implicit model currently employed by a number of existing implementations and adds explicit formatting characters for special circumstances. In most cases, there is no need to include additional information with the text to obtain correct display ordering.
However, in the case of bidirectional text, there are circumstances where an implicit bidirectional ordering is not sufficient to produce comprehensible text. To deal with these cases, a minimal set of directional formatting characters is defined to control the ordering of characters when rendered. This allows exact control of the display ordering for legible interchange and ensures that plain text used for simple items like filenames or labels can always be correctly ordered for display.
为什么要创建像 this 这样的算法?
the bidi algorithm can render a sequence of Arabic or Hebrew
characters one after the other from right to left.
这里有不可见的字符会改变代码的显示方式。在 Intellij 中,可以通过将代码复制粘贴到一个空字符串 (""
) 中找到它们,这会用 Unicode 转义符替换它们,移除它们的影响并显示编译器看到的顺序。
这是复制粘贴的输出:
"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
,它是从左到右的覆盖,开始一个嵌套块,其中所有字符都被强制按从左到右的顺序,覆盖第一个覆盖。
所以显示原代码时,class M
正常显示,但是\u202E
将所有内容的显示顺序颠倒,从那里到\u202D
,再次颠倒一切. (正式地,从 \u202D
到行终止符的所有内容都被反转两次,一次是由于 \u202D
,一次是由于 \u202E
,其余文本被反转,这就是为什么文本显示在行的中间而不是末尾。)由于行终止符,下一行的方向性独立于第一行处理,因此 {'H','e','l','l','o',' ','W','o','r','l','d','!'});}}
正常显示。
完整的(极其复杂,长达几十页)Unicode 双向算法,参见Unicode Standard Annex #9。
因为 Unicode Bidirectional Algorithm 看起来不一样。 Unicode 双向算法使用 RLO 和 LRO 两个不可见字符来改变嵌套在这两个元字符之间的字符的视觉外观。
结果是视觉上它们看起来是倒序的,但实际字符在记忆中并没有倒序。您可以分析结果here。 Java 编译器将忽略 RLO 和 LRO,并将它们视为空白,这就是代码编译的原因。
注 1:文本编辑器和浏览器使用此算法来直观地显示 LTR 字符(英文)和 RTL 字符(例如
阿拉伯语,希伯来语)同时在一起 - 因此 "bi"-定向。您可以阅读有关双向算法的更多信息
在 Unicode 的 website.
注 2:LRO 和 RLO 的确切行为在 Section 2.2 中定义
算法。
Chapter 3 of the language specification 通过详细描述如何为 Java 程序完成词汇翻译来提供解释。问题最重要的是什么:
Programs are written in Unicode (§3.1), but lexical translations are provided (§3.2) so that Unicode escapes (§3.3) can be used to include any Unicode character using only ASCII characters.
所以一个程序是用Unicode字符写的,如果文件编码不支持Unicode字符,作者可以使用\uxxxx
转义它们,在这种情况下它会被翻译成合适的字符。本例中出现的 Unicode 字符之一是 \u202E
。它在代码段中没有直观显示,但如果您尝试切换浏览器的编码,隐藏字符可能会出现。
因此,class 声明中的词法翻译结果:
class M\u202E{
表示class标识符是M\u202E
。 specification 认为这是一个有效的标识符:
Identifier:
IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
IdentifierChars:
JavaLetter {JavaLetterOrDigit}
A "Java letter-or-digit" is a character for which the method Character.isJavaIdentifierPart(int)
returns true.
这实际上是因为 Unicode 双向支持。
U+202E RIGHT TO LEFT OVERRIDE
U+202D LEFT TO RIGHT OVERRIDE
所以,这些都是一些棘手的角色。它们实际上是为从右到左的语言支持而定义的。真正的代码是
class M<U+202E>{public static void main(String[]a<U+202D>){System.out.print(new char[]
{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}
(通过粘贴到 cmd.exe 得到这个)。希望这个答案能帮助您了解它是如何工作的。
下面是我在网上找到的一些代码:
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!
打印到屏幕上;你可以看到它 运行 here。我可以清楚地看到写着 public static void main
,但是它是倒着的。这段代码是如何工作的?这甚至如何编译?
编辑: 我在 IntellIJ 中试过这段代码,它工作正常。但是,由于某种原因,它在 notepad++ 和 cmd 中不起作用。我还没有找到解决办法,所以如果有人找到了,请在下面评论。
字符U+202E
从右到左镜像代码,虽然很聪明。隐藏在M开头,
"class M\u202E{..."
How did I found the magic behind this?
好吧,一开始我看到这个问题的时候我很难,"it's a kind of joke, to lose somebody else time",但是后来,我打开了我的IDE("IntelliJ"),创建了一个class,并通过代码... 并编译!!! 所以,我仔细看了一下,发现 "public static void" 是向后的,所以我用光标去那里, 并删除了几个字符 ... 会发生什么? 字符开始向后擦除,所以,我想嗯....很少见...我必须执行它...所以我继续执行程序,但首先我需要保存它...那是我找到它的时候!。我无法保存文件,因为我的 IDE 说某些字符有不同的编码, 并指出它在哪里 ,所以我开始研究 Google 用于可以完成这项工作的特殊字符,仅此而已:)
A little about
Unicode 双向算法,U+202E
涉及,简要说明 explain:
The Unicode Standard prescribes a memory representation order known as logical order. When text is presented in horizontal lines, most scripts display characters from left to right. However, there are several scripts (such as Arabic or Hebrew) where the natural ordering of horizontal text in display is from right to left. If all of the text has a uniform horizontal direction, then the ordering of the display text is unambiguous.
However, because these right-to-left scripts use digits that are written from left to right, the text is actually bi-directional: a mixture of right-to-left and left-to-right text. In addition to digits, embedded words from English and other scripts are also written from left to right, also producing bidirectional text. Without a clear specification, ambiguities can arise in determining the ordering of the displayed characters when the horizontal direction of the text is not uniform.
This annex describes the algorithm used to determine the directionality for bidirectional Unicode text. The algorithm extends the implicit model currently employed by a number of existing implementations and adds explicit formatting characters for special circumstances. In most cases, there is no need to include additional information with the text to obtain correct display ordering.
However, in the case of bidirectional text, there are circumstances where an implicit bidirectional ordering is not sufficient to produce comprehensible text. To deal with these cases, a minimal set of directional formatting characters is defined to control the ordering of characters when rendered. This allows exact control of the display ordering for legible interchange and ensures that plain text used for simple items like filenames or labels can always be correctly ordered for display.
为什么要创建像 this 这样的算法?
the bidi algorithm can render a sequence of Arabic or Hebrew characters one after the other from right to left.
这里有不可见的字符会改变代码的显示方式。在 Intellij 中,可以通过将代码复制粘贴到一个空字符串 (""
) 中找到它们,这会用 Unicode 转义符替换它们,移除它们的影响并显示编译器看到的顺序。
这是复制粘贴的输出:
"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
,它是从左到右的覆盖,开始一个嵌套块,其中所有字符都被强制按从左到右的顺序,覆盖第一个覆盖。
所以显示原代码时,class M
正常显示,但是\u202E
将所有内容的显示顺序颠倒,从那里到\u202D
,再次颠倒一切. (正式地,从 \u202D
到行终止符的所有内容都被反转两次,一次是由于 \u202D
,一次是由于 \u202E
,其余文本被反转,这就是为什么文本显示在行的中间而不是末尾。)由于行终止符,下一行的方向性独立于第一行处理,因此 {'H','e','l','l','o',' ','W','o','r','l','d','!'});}}
正常显示。
完整的(极其复杂,长达几十页)Unicode 双向算法,参见Unicode Standard Annex #9。
因为 Unicode Bidirectional Algorithm 看起来不一样。 Unicode 双向算法使用 RLO 和 LRO 两个不可见字符来改变嵌套在这两个元字符之间的字符的视觉外观。
结果是视觉上它们看起来是倒序的,但实际字符在记忆中并没有倒序。您可以分析结果here。 Java 编译器将忽略 RLO 和 LRO,并将它们视为空白,这就是代码编译的原因。
注 1:文本编辑器和浏览器使用此算法来直观地显示 LTR 字符(英文)和 RTL 字符(例如
阿拉伯语,希伯来语)同时在一起 - 因此 "bi"-定向。您可以阅读有关双向算法的更多信息
在 Unicode 的 website.
注 2:LRO 和 RLO 的确切行为在 Section 2.2 中定义
算法。
Chapter 3 of the language specification 通过详细描述如何为 Java 程序完成词汇翻译来提供解释。问题最重要的是什么:
Programs are written in Unicode (§3.1), but lexical translations are provided (§3.2) so that Unicode escapes (§3.3) can be used to include any Unicode character using only ASCII characters.
所以一个程序是用Unicode字符写的,如果文件编码不支持Unicode字符,作者可以使用\uxxxx
转义它们,在这种情况下它会被翻译成合适的字符。本例中出现的 Unicode 字符之一是 \u202E
。它在代码段中没有直观显示,但如果您尝试切换浏览器的编码,隐藏字符可能会出现。
因此,class 声明中的词法翻译结果:
class M\u202E{
表示class标识符是M\u202E
。 specification 认为这是一个有效的标识符:
Identifier:
IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
IdentifierChars:
JavaLetter {JavaLetterOrDigit}
A "Java letter-or-digit" is a character for which the method
Character.isJavaIdentifierPart(int)
returns true.
这实际上是因为 Unicode 双向支持。
U+202E RIGHT TO LEFT OVERRIDE
U+202D LEFT TO RIGHT OVERRIDE
所以,这些都是一些棘手的角色。它们实际上是为从右到左的语言支持而定义的。真正的代码是
class M<U+202E>{public static void main(String[]a<U+202D>){System.out.print(new char[]
{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}
(通过粘贴到 cmd.exe 得到这个)。希望这个答案能帮助您了解它是如何工作的。