以下是我在互联网上找到的一些代码:
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一起在记事本++中工作。我仍然没有找到解决方案,所以如果有人这样做,请在下面评论。
- 这个很有趣…与RTL支持有关吗?
- @ EugeneSh。不太确定您所说的RTL是什么意思,但我不知道这段代码为什么有效。只是在网上的某个地方看到的。
- 从右到左(阿拉伯语或希伯来语等语言)
- 有一个unicode字符8237;在M之后,也在[]a之后:fileformat.info/info/unicode/char/202d/index.htm,它被称为从左到右的覆盖。
- 实际上是"你好,世界!"不是"你好世界"。
- 强制性xkcd:xkcd.com/1137
- 通过使用鼠标在代码片段中进行选择,您可以很容易地看到这里发生了什么。
- @AndreasRejbrand是的,我在发布问题后意识到:)
- niam diov citats cilbup听起来像拉丁谚语。
- 你可能会发现这篇关于202E等Unicode字符的2012年文章很有趣。vanillajava.blogspot.co.uk/2012/09/hidden-code.html
- 看不见的标识符,哈!谈论模糊的代码:)@peterlawrey
- vanillajava.blogspot.co.uk/2012/08/…更多角色乐趣
- 用鼠标标记示例代码会显示RTL效果(至少在使用ff55的Windows上)。
这里有一些不可见的字符可以改变代码的显示方式。在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字符本身所做的操作。我可能会完全忽略它们(或者将它们视为空白),或者它可能会将它们解释为对源代码的实际贡献。我不知道这里的Java规则,但是事实上,它们被放置在其他未使用的标识符的结尾,提示我可能是后者,Unicode字符实际上是这些标识符名称的一部分。
- 出于兴趣,这种方法在C中是否也同样有效?
- @IANF1可以在编译器/解释器将RTL和LTR字符计算为空白的任何语言中工作。但是,如果你重视下一个人接触你的代码的明智性,那就不要在生产代码中这样做。
- 或者,换言之:"编码的时候,就好像维护编码的人是一个暴力的精神病患者,他知道你住在哪里。",@ianf1。或者:"代码编写时,如果最终维护代码的人在堆栈溢出时将您命名为原始作者,并使您感到羞耻。"
由于采用了Unicode双向算法,它看起来不同。Unicode双向算法使用RLO和LRO的两个不可见字符来更改嵌套在这两个元字符之间的字符的视觉外观。
结果是,它们在视觉上看起来是相反的顺序,但内存中的实际字符并没有颠倒。你可以在这里分析结果。Java编译器将忽略RLO和LRO,并将它们视为空白,这就是代码编译的原因。
注1:文本编辑器和浏览器使用此算法以可视方式显示LTR字符(英语)和RTL字符(例如阿拉伯语、希伯来语)同时在一起-因此是双向的。您可以阅读有关双向算法的更多信息在Unicode的网站上。注2:LRO和RLO的确切行为见第2.2节算法。
- 这种能力的目的是什么?
- 有时需要这些字符来正确地呈现阿拉伯语和希伯来语。这些语言是从右向左(RTL)读写的,读/写的第一个字符出现在右侧。你可以在这里读到更多。
- 阿拉伯语和希伯来语字符本质上是RTL,不过-即使没有显式覆盖,它们也会出现RTL,而且它们甚至会自动颠倒附近某些其他字符的顺序,我认为主要是标点符号-因此显式覆盖很少必要。
- 此处的此页说明何时需要覆盖。@用户2357112是对的,他们很少需要。事实上,当你有标点、引号和数字时,这些特殊字符被认为是"中性的"。对于一台无法阅读单词和理解上下文的计算机来说,是否将它们视为LTR或RTL尚不清楚,但是bidi算法必须选择一些顺序。有时它会"出错",您需要使用这些重写字符来"更正它"。
- 此外,u+202e和u+202d不被视为空白。Java只考虑ASCII空间、水平选项卡、表单提要和CR/LF/CRLF作为空白。它们实际上是标识符M\u202E和a\u202D的词汇部分,但这些标识符似乎被视为等同于M和a。(JLS没有很好地解释这一点。)
- @詹姆斯劳森:我是唯一一个认为把这种表达层面的问题混为一谈的人吗?文本布局算法需要能够识别"单词",这些单词可以以上下文无关的方式布局,但就我所知,Unicode可能需要应用程序扫描一段文本前后的许多字符,以确定是否需要将其拆分为两个单词。
- @超级卫星不,你不是唯一的一个:在我看来,这些覆盖字符应该作为最后的手段。例如,在HTML5中,您可以使用来完成与rlo相同的任务。没错,bidi算法使用大量的字符扫描和处理来计算布局。大多数操作系统和浏览器使用一个叫做ICU BIDI的快速C/C++库来完成所有这些工作。Google Chrome有自己的实现renderText。
- @詹姆斯劳森:实际上,显式重写字符并不是真正的问题,除非它们应该被认为是语义的而不是表象的。大多数包含希伯来语字符的文本都有从右到左的顺序,但有些文本的顺序是相反的,这样,至少在没有换行符的情况下,它们会在从左到右显示时正确呈现。知道给定字符串中出现哪种字符是有用的。我建议出于许多目的,"程序员的文本编辑器"应该按LTR顺序显示所有内容,或者按RTL顺序显示所有内容,但是……
- @supercat:unicode双向性不影响分词。至于演示级别的问题,很难说。空格和换行符和双向性一样是一个表示级别的问题,但是我们已经习惯了那些作为ASCII的一部分的内容,以至于我们几乎都不会遇到这种情况。(换行符不总是字符!)对于表示级别的问题,我们没有单独的统一表示标准,因此将双向性作为Unicode的一部分至少为我们提供了标准的语义和处理它的标准工具。
- …在某些时尚中,表达顺序与逻辑顺序不匹配。如果将"单词"视为可以使用其各自转义连续显示的字符序列,那么双向性可以创建分词,因为像12+34这样的字符序列如果出现在LTR文本中,将是单个单词,如果出现在RTL文本中,则是三个单词。
- @supercat:"可以使用它们各自的转义连续显示"听起来像是应用于任何任意的LTR文本,比如整个段落,所以它看起来不像是一个有用的"单词"概念。如果你的意思是其他的,我不认为你的意思实际上会改变基于双向性的字数。(不过,我们正接近"评论不适合扩展讨论"的领域。)
- @用户2357112:一个单向段落可以通过将其细分为单词,将每个单词呈现为一个框,然后排列框来流动。在一个框中呈现文本的代码需要知道诸如紧排之类的事情,但是布局代码对于框的内容可能是不可知的。用EDOCX1[1]进行测试,我似乎忘记了确切的规则,但是用一个符号替换+符号,这一点会变得更清楚。取x 12&34 y 12&34用alef代替y,得到x 12&34 ? 12&34。字符12&34是否连续显示…
- …取决于前面的alef字符的存在。如果在所有不能以统一方向呈现的文本中都需要标记字符,布局函数将不需要知道任何规则,而不需要知道如何识别这些标记。事实上,我不知道如何用类似javascript的代码来编写布局引擎,而不必硬编码一大堆Unicode方向性规则。
- @supercat:但是双向性不会影响到在那里划分成盒子。它改变了框的排列顺序和框中字符的顺序,但不改变框中的分隔。在第二个版本中,12&34仍然是一个框,即使框中的字形以新的顺序显示。例如,该算法不会比以前更急于在框中插入换行符。
- @supercat有时需要将ltr文本嵌入到rtl语句中,该语句将放在长ltr段落的引号中。在那些深藏的情况下,计算机帮不了你。或者,即使是在这些示例中这样的简单情况下,您也需要覆盖LTR/RTL设置。
- @ L?紫外线?nhph&250;c:将rtl文本嵌入到ltr语句中,或者反之亦然,应插入标记以切换方向,插入文本时应在应用程序级别进行调整。此外,还应该有一个标准的函数来获取一段文本,并将所有字符重新排列成它们应该从左到右或从右到左的顺序,然后应该有一种按该顺序显示字符的方法。给定这样一个函数,一个函数(例如在曲线上环绕文本)可以很容易地容纳LTR和RTL脚本的混合。
- @ L?紫外线?nhph&250;c:事实上,我不知道一个函数用什么实际的方法将文本环绕在曲线上,可以很容易地找出任何特定字符应该放在哪里,或者哪个字符应该跟在另一个字符后面,除非它包含大量与Unicode字符方向相关的硬编码逻辑。
字符U+202E从右到左映射代码,但它非常聪明。从M开始隐藏,
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。它不会在代码段中显示,但如果尝试切换浏览器的编码,则可能会显示隐藏字符。
因此,词汇翻译导致类声明:
这意味着类标识符是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.
- 抱歉,这是倒退(双关语是故意的)。源代码中没有转义;您正在描述如何编写它。它编译成一个名为"m"的类(只有一个字符)。
- @tomblodget确实如此,但其要点(事实上我在规范引号中强调了这一点)是编译器还可以处理原始的Unicode字符。这就是全部的解释。转义翻译只是一个附加信息,与本案无关。至于编译类,我认为这是因为编译器以某种方式丢弃了rtl开关字符。我将尝试看看这是否是预期的,但我认为发生在词汇翻译阶段之后。