为什么这个代码向后写,打印"Hello World!"

Ima*_*kin 252 java unicode right-to-left

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

class M?{public static void main(String[]a?){System.out.print(new char[]
{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}    
Run Code Online (Sandbox Code Playgroud)

此代码打印Hello World!到屏幕上; 你可以看到它在这里运行.我可以清楚地看到public static void main写作,但它是倒退的.这段代码是如何工作的?这甚至如何编译?

编辑:我在IntellIJ中尝试了这个代码,它运行正常.但是,由于某种原因,它在记事本++和cmd中都不起作用.我仍然没有找到解决方案,所以如果有人这样做,请在下面评论.

Dav*_*oda 245

这里有隐形字符可以改变代码的显示方式.在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','!'});}}   "
Run Code Online (Sandbox Code Playgroud)

源代码字符按此顺​​序存储,编译器将它们按此顺序处理,但它们的显示方式不同.

注意\u202E字符,这是一个从右到左的覆盖,启动一个块,其中所有字符都被强制从右到左显示,并且\u202D,这是一个从左到右的覆盖,启动一个嵌套块,其中所有字符被强制为从左到右的顺序,覆盖第一个覆盖.

当Ergo显示原始代码时,class M它会正常显示,但会\u202E反转从那里到所有内容的显示顺序\u202D,这会再次反转所有内容.(形式上,从\u202D行终止符到行终止符的所有内容都会被反转两次,一次是由于\u202D其中一段时间​​与文本的其余部分相反\u202E,这就是为什么这个文本显示在行的中间而不是结尾.)由于行终止符,下一行的方向性由第一行独立处理,因此{'H','e','l','l','o',' ','W','o','r','l','d','!'});}}正常显示.

对于完整(极其复杂,数十页长)的Unicode双向算法,请参阅Unicode标准附件#9.

  • @ IanF1它适用于编译器/解释器将RTL和LTR字符计为空格的任何语言.但是,如果您完全重视下一个人触摸您的代码的理智,那么就不要在生产代码中执行此操作,这很可能就是您. (12认同)
  • 或者,换句话说:["总是编码,好像最终维护你的代码的人是一个知道你住在哪里的暴力精神病患者."](http://wiki.c2.com/?CodeForTheMaintainer),@ IanF1.或者也许:"总是编写代码,好像最终维护代码的人会在Stack Overflow上作为原始作者而羞辱你." (2认同)

Jam*_*son 43

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

结果是在视觉上它们以相反的顺序查看,但是内存中的实际字符不会反转.您可以在此处分析结果.Java编译器将忽略RLO和LRO,并将它们视为空格,这就是代码编译的原因.

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

  • 有时需要这些字符才能正确地呈现阿拉伯语和希伯来语.这些语言是从右到左*(RTL)读写的,第一个读/写字符出现在*右侧*.您可以阅读更多[此处](https://www.w3.org/International/articles/inline-bidi-markup/uba-basics). (6认同)
  • 此外,U + 202E和U + 202D不被视为空白.Java仅将[ASCII空格,水平制表符,换页符和CR/LF/CRLF视为空格](https://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#jls -3.6).它们实际上是词法上的标识符"M\u202E"和"a\u202D"的一部分,但这些标识符似乎被视为等同于"M"和"a".(JLS没有很好地解释这一点.) (3认同)

Dam*_*ero 28

角色U+202E从右到左反映了代码,但它非常聪明.隐藏在M中,

"class M\u202E{..."
Run Code Online (Sandbox Code Playgroud)

我是如何找到这背后的魔力的

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

一点点

Unicode双向算法,并U+202E参与其中,简要说明:

Unicode标准规定了称为逻辑顺序的内存表示顺序.当文本以水平线显示时,大多数脚本从左到右显示字符.但是,有几个脚本(如阿拉伯语或希伯来语),其中显示的水平文本的自然顺序是从右到左.如果所有文本具有统一的水平方向,则显示文本的排序是明确的.

但是,因为这些从右到左的脚本使用从左到右书写的数字,所以文本实际上是双向的:从右到左和从左到右文本的混合.除了数字之外,英语和其他脚本中的嵌入词也从左到右书写,也产生双向文本.如果没有明确的规范,当文本的水平方向不均匀时,在确定所显示的字符的排序时会出现歧义.

本附件描述了用于确定双向Unicode文本的方向性的算法.该算法扩展了许多现有实现当前使用的隐式模型,并为特殊情况添加了显式格式化字符.在大多数情况下,不需要在文本中包含其他信息以获得正确的显示顺序.

但是,在双向文本的情况下,存在隐式双向排序不足以产生可理解文本的情况.为了处理这些情况,定义了一组最小的方向格式化字符来控制渲染时字符的排序.这样可以精确控制清晰易读的显示顺序,并确保用于文件名或标签等简单项目的纯文本始终可以正确排序显示.

为什么要建立一些算法,像这样

bidi算法可以从右到左依次渲染一系列阿拉伯语或希伯来语字符.

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


man*_*uti 5

语言规范的第 3 章通过详细描述如何为 Java 程序完成词法转换来提供解释。这个问题最重要的是什么:

程序是用 Unicode (§3.1) 编写的,但提供了词法翻译(§3.2),因此可以使用 Unicode 转义 (§3.3) 来包含任何仅使用 ASCII 字符的 Unicode 字符。

所以程序是用 Unicode 字符编写的,\uxxxx如果文件编码不支持 Unicode 字符,作者可以使用它们进行转义,在这种情况下,它被转换为适当的字符。在这种情况下出现的 Unicode 字符之一是\u202E. 它没有在代码段中直观地显示出来,但是如果您尝试切换浏览器的编码,则可能会出现隐藏字符。

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

class M\u202E{
Run Code Online (Sandbox Code Playgroud)

这意味着类标识符是M\u202E. 该规范认为这是有效的IDENTIFER:

Identifier:
    IdentifierChars but not a Keyword or BooleanLiteral or NullLiteral
IdentifierChars:
    JavaLetter {JavaLetterOrDigit}
Run Code Online (Sandbox Code Playgroud)

“Java 字母或数字”是方法Character.isJavaIdentifierPart(int)返回 true的字符。