关于Java:为什么这个代码,向后写,打印”Hello World”!”

Why does this code, written backwards, print “Hello World!”

以下是我在互联网上找到的一些代码:

1
2
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的书写,但它是向后的。这个代码是如何工作的?这是如何编译的?

编辑:我在intellij中尝试过这个代码,它工作得很好。但是,由于某些原因,它不能与cmd一起在记事本++中工作。我仍然没有找到解决方案,所以如果有人这样做,请在下面评论。


这里有一些不可见的字符可以改变代码的显示方式。在intellij中,可以通过将代码复制粘贴到空字符串(""中)来找到这些代码,该字符串用unicode转义符替换它们,删除它们的效果并显示编译器看到的顺序。

下面是该复制粘贴的输出:

1
2
3
"class M\u202E{public static void main(String[]a\u202D){System.out.print(new char[]
"
+
       "{'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标准附录9。


由于采用了Unicode双向算法,它看起来不同。Unicode双向算法使用RLO和LRO的两个不可见字符来更改嵌套在这两个元字符之间的字符的视觉外观。

结果是,它们在视觉上看起来是相反的顺序,但内存中的实际字符并没有颠倒。你可以在这里分析结果。Java编译器将忽略RLO和LRO,并将它们视为空白,这就是代码编译的原因。

注1:文本编辑器和浏览器使用此算法以可视方式显示LTR字符(英语)和RTL字符(例如阿拉伯语、希伯来语)同时在一起-因此是双向的。您可以阅读有关双向算法的更多信息在Unicode的网站上。注2:LRO和RLO的确切行为见第2.2节算法。


字符U+202E从右到左映射代码,但它非常聪明。从M开始隐藏,

1
"class M\u202E{..."

How did I found the magic behind this?

好吧,起初当我看到这个问题时,我很强硬,"这是一种玩笑,浪费别人的时间",但后来,我打开了我的IDE("intellij"),创建了一个类,并通过了代码……它编译了!!!!所以,我看了看,发现"公共静态空间"是向后的,所以我用光标去了那里,删除了一些字符…会发生什么?字符开始向后擦除,所以,我想…稀有的…我必须执行它…所以我继续执行程序,但首先我需要保存它…我就是在那时找到的!.I无法保存该文件,因为我的IDE说某个字符有不同的编码,并指出了它在哪里,所以我在Google中开始了一项研究,寻找可以完成这项工作的特殊字符,就这样了:)

A little about

Unicode双向算法和涉及的U+202E简要说明:

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.

为什么要创建这样的算法?

the bidi algorithm can render a sequence of Arabic or Hebrew
characters one after the other from right to left.

P.S.:我知道这不是最好的答案,但先解决这个问题很有趣:P


语言规范的第3章通过详细描述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。它不会在代码段中显示,但如果尝试切换浏览器的编码,则可能会显示隐藏字符。

因此,词汇翻译导致类声明:

1
class M\u202E{

这意味着类标识符是M\u202E。规范认为这是一个有效的标识符:

1
2
3
4
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.