Reg*_*Reg 1326 java unicode comments
以下代码生成输出"Hello World!" (不,真的,试试吧).
public static void main(String... args) {
// The comment below is not a typo.
// \u000d System.out.println("Hello World!");
}
Run Code Online (Sandbox Code Playgroud)
原因是Java编译器将Unicode字符解析\u000d
为新行并转换为:
public static void main(String... args) {
// The comment below is not a typo.
//
System.out.println("Hello World!");
}
Run Code Online (Sandbox Code Playgroud)
从而导致评论被"执行".
由于这可以用来"隐藏"恶意代码或恶意程序员可以设想的任何东西,为什么在评论中允许它?
为什么Java规范允许这样做?
aio*_*obe 729
Unicode解码在任何其他词汇翻译之前进行.这样做的主要好处是可以在ASCII和任何其他编码之间来回切换.你甚至不需要弄清楚评论的开始和结束位置!
如JLS第3.3节所述,这允许任何基于ASCII的工具处理源文件:
[...] Java编程语言指定了一种将用Unicode编写的程序转换为ASCII的标准方法,该程序将程序更改为可由基于ASCII的工具处理的形式.[...]
这为平台独立性(支持的字符集的独立性)提供了基本保证,这一直是Java平台的关键目标.
能够在文件中的任何位置编写任何Unicode字符是一个简洁的功能,在使用非拉丁语言编写代码时,在评论中尤其重要.它以这种微妙的方式干扰语义的事实只是(不幸的)副作用.
关于这个主题有许多问题,Joshua Bloch和Neal Gafter的Java Puzzlers包括以下变体:
这是一个合法的Java程序吗?如果是这样,它会打印什么?
Run Code Online (Sandbox Code Playgroud)\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020\u0020 \u0063\u006c\u0061\u0073\u0073\u0020\u0055\u0067\u006c\u0079 \u007b\u0070\u0075\u0062\u006c\u0069\u0063\u0020\u0020\u0020 \u0020\u0020\u0020\u0020\u0073\u0074\u0061\u0074\u0069\u0063 \u0076\u006f\u0069\u0064\u0020\u006d\u0061\u0069\u006e\u0028 \u0053\u0074\u0072\u0069\u006e\u0067\u005b\u005d\u0020\u0020 \u0020\u0020\u0020\u0020\u0061\u0072\u0067\u0073\u0029\u007b \u0053\u0079\u0073\u0074\u0065\u006d\u002e\u006f\u0075\u0074 \u002e\u0070\u0072\u0069\u006e\u0074\u006c\u006e\u0028\u0020 \u0022\u0048\u0065\u006c\u006c\u006f\u0020\u0077\u0022\u002b \u0022\u006f\u0072\u006c\u0064\u0022\u0029\u003b\u007d\u007d
(这个程序原来是一个简单的"Hello World"程序.)
在解决益智游戏的过程中,他们指出了以下内容:
更严重的是,这个谜题有助于强化前三个课程的教训:当您需要插入无法以任何其他方式表示到您的程序中的字符时,Unicode转义是必不可少的.在所有其他情况下避免它们.
Hol*_*ger 138
由于尚未解决,这里有一个解释,为什么Unicode转义的转换发生在任何其他源代码处理之前:
它背后的想法是它允许在不同的字符编码之间无损地翻译Java源代码.今天,有广泛的Unicode支持,这看起来不是一个问题,但是当时西方国家的开发人员从他的亚洲同事那里收到一些包含亚洲字符的源代码并不容易做出一些改变(包括编译和测试它并将结果发回,所有这些都不会损坏.
因此,Java源代码可以用任何编码编写,并允许标识符,字符,String
文字和注释中的各种字符.然后,为了无损地传输它,目标编码不支持的所有字符都被它们的Unicode转义替换.
这是一个可逆的过程,有趣的是,转换可以通过一个工具来完成,该工具不需要知道任何关于Java源代码语法的知识,因为转换规则不依赖于它.这适用于编译器内部实际Unicode字符的转换也独立于Java源代码语法.这意味着您可以在两个方向上执行任意数量的转换步骤,而无需更改源代码的含义.
这就是另一个奇怪的功能,甚至没有提到的原因:\uuuuuuxxxx
语法:
当翻译工具转义字符并遇到已经是转义序列的序列时,它应该u
在序列中插入一个额外的转换\ucafe
为\uucafe
.意思不会改变,但是当转换到另一个方向时,工具应该只删除一个u
并仅用u
Unicode字符替换包含单个序列的序列.这样,即使Unicode转义在来回转换时也会以原始形式保留.我想,没有人曾经使用过这个功能......
Pep*_*itz 104
我将完全无效地添加这一点,只是因为我无法帮助自己,我还没有看到它,但问题是无效的,因为它包含一个错误的隐藏前提,即代码在一条评论!
在Java源代码中,\ u000d在各方面都与ASCII CR字符等效.它是一个结尾的行,简单明了,无论它出现在哪里.问题中的格式是误导性的,字符序列实际上在语法上对应的是:
public static void main(String... args) {
// The comment below is no typo.
//
System.out.println("Hello World!");
}
Run Code Online (Sandbox Code Playgroud)
恕我直言,最正确的答案是:代码执行,因为它不在评论中; 它在下一行.Java中不允许"在注释中执行代码",就像您期望的那样.
大部分混淆源于语法高亮显示器和IDE不够复杂以考虑这种情况.它们要么根本不处理unicode转义,要么在解析代码之后而不是之前处理它,就像javac
它一样.
zwo*_*wol 65
所述\u000d
逸出终止评论,因为\u
逃逸均匀地转化为相应的Unicode字符之前被标记化的程序.你同样可以使用\u0057\u0057
的,而不是//
来开始评论.
这是IDE中的一个错误,它应该语法突出显示该行,以明确\u000d
结束注释.
这也是语言中的设计错误.它现在无法纠正,因为这会破坏依赖它的程序. \u
转换器应该仅由编译器在"有意义"(字符串文字和标识符,可能不在其他地方)的上下文中转换为相应的Unicode字符,或者它们应该被禁止在U + 0000-007F范围内生成字符, 或两者.这些语义中的任何一个都会阻止注释被\u000d
转义终止,而不会干扰\u
转义有用的情况 - 请注意,这包括\u
在注释中使用转义作为在非拉丁文脚本中编码注释的方法,因为文本编辑器可以更广泛地了解\u
转义的重要性,而不是编译器.(我不知道任何编辑器或IDE会\u
在任何上下文中显示转义为相应的字符.)
在C系列中存在类似的设计错误,1其中在确定注释边界之前处理反斜杠换行符,例如
// this is a comment \
this is still in the comment!
Run Code Online (Sandbox Code Playgroud)
我提出这个问题来说明这个特定的设计错误很容易发生,而且如果你习惯于考虑标记化和解析编译程序员的思维方式,那么直到修正它为时已经太晚才会发现它是错误的.关于标记化和解析.基本上,如果你已经定义了你的形式语法,然后有人想出一个语法特殊情况 - trigraphs,反斜杠换行,在源文件中编码任意Unicode字符,限制为ASCII,无论什么 - 需要楔入,它更容易在令牌化器之前添加转换传递,而不是重新定义令牌化器以注意使用该特殊情况的合理位置.
1对于学龄儿童:我知道C的这个方面是100%有意识的,理由是 - 我不是这样做的 - 它可以让你用任意长线机械强制编码代码到打孔卡上.这仍然是一个不正确的设计决定.
Jon*_*ons 21
这是一个有意的设计选择,一直回到Java的原始设计.
对于那些问"谁想要在评论中逃脱Unicode?"的人,我认为他们是那些母语使用拉丁字符集的人.换句话说,Java的原始设计中固有的,人们可以在Java程序中的任何合法地方使用任意Unicode字符,最常见的是在注释和字符串中.
可以说,用于查看源文本的程序(如IDE)的缺点是这些程序无法解释Unicode转义并显示相应的字形.
Zho*_*gYu 20
我同意@zwol这是一个设计错误; 但我更加批评它.
\u
escape在string和char文字中很有用; 这是唯一应该存在的地方.它应该像其他逃脱一样处理\n
; 并且"\u000A"
应该完全意味着"\n"
.
\uxxxx
在评论中绝对没有意义- 没有人可以阅读.
同样,\uxxxx
在程序的其他部分也没有意义.唯一的例外可能是在强制包含一些非ascii字符的公共API中 - 我们最后一次看到它是什么?
设计师在1995年有他们的理由,但20年后,这似乎是一个错误的选择.
(向读者提问 - 为什么这个问题不断获得新的选票?这个问题是否从流行的地方联系起来?)
Mar*_*ijn 11
能够回答为什么Unicode转义被实现的唯一的人是编写规范的人.
一个似是而非的理由是,希望允许整个BMP成为Java源代码的可能字符.这提出了一个问题:
当Unicode转义进入战斗时,这是非常困难的:它创建了一整套新的词法分析器规则.
最简单的方法是分两步执行lexing:首先使用它所代表的字符搜索并替换所有Unicode转义符,然后解析生成的文档,就好像Unicode转义不存在一样.
这样做的好处在于它易于指定,因此它使规范更简单,并且易于实现.
不好的是,你的榜样.
归档时间: |
|
查看次数: |
73818 次 |
最近记录: |