2016-03-09 63 views
5

前几天,有人问我关于这个程序的输出:的Unicode逃逸行为

public static void main(String[] args) { 
    // \u0022 is the Unicode escape for double quote (") 
    System.out.println("a\u0022.length() + \u0022b".length()); 
} 

我首先想到的是这个程序应该打印a\u0022.length() + \u0022b长度,这是16,但奇怪的是,它印2 。我知道\u0022"的Unicode,但我认为这"将被转义并且仅代表一个"文字,没有特别的含义。而在现实中,Java的莫名其妙解析这个字符串如下:

System.out.println("a".length() + "b".length()); 

我不能换我的头周围这个怪异的行为,为什么Unicode转义字符不表现为正常的转义序列?

更新显然,这是Joshua Bloch和Neal Gafter编写的Java Puzzlers: Traps, Pitfalls, and Corner Cases书的脑筋急转弯之一。更具体地说,这个问题涉及到拼图14:逃避路途

+4

http://www.javajee.com/unicode-escapes-in-java – ShrtTth

回答

6

为什么Unicode转义不表现为正常的转义序列?

基本上,它们在阅读输入时处于不同的位置 - 在lexing而不是解析中,如果我的术语是正确的话。它们不是字符文字或字符串中的转义序列,它们是整个源文件的转义序列。任何不属于Unicode转义序列的字符都可以用Unicode转义序列替换。所以你可以完全用ASCII编写程序,它实际上有非ASCII的变量,方法和类名...

基本上我相信这是Java中的一个设计错误,因为它会导致一些非常奇怪的效果例如,如果你有//评论中的换行符的转义序列...),但它是...

section 3.3 of the JLS详述:

一种Java编程语言(“Java编译器”)首次承认Unicode转义字符的输入中,翻译的ASCII字符\ u加上四个十六进制数字的编译器UTF-16代码单元(§3.1)用于指定的十六进制值,并且不改变所有其他字符。表示补充字符需要两次连续的Unicode转义。该翻译步骤产生一系列Unicode输入字符。

...

Java编程语言指定转化写的Unicode到ASCII一个程序,改变程序转变为可以通过基于ASCII的工具处理的形式的标准方式。转换包括将程序的源文本中的任何Unicode转义转换为ASCII,方法是添加一个额外的u(例如,\ uxxxx成为\ uuxxxx),同时将源文本中的非ASCII字符转换为每个包含一个u的Unicode转义。

这个转换后的版本同样可以被Java编译器接受并代表完全相同的程序。通过将多个u存在的每个转义序列转换为一个少于u的Unicode字符序列,同时将每个转义序列用单个u转换为相应的单个Unicode字符,以后可以从此ASCII表单中恢复确切的Unicode源。

7

之前的编译器实际上会将源为字节码,词汇翻译阶段将会把声明:

System.out.println("a\u0022.length() + \u0022b".length()); 

到:

System.out.println("a".length() + "b".length()); 

因此,结果是2

而且请参见语言规范的this section about lexical translation

原始Unicode字符流被转换成标记序列,使用以下三个词汇翻译步骤,这些步骤依次施加:

  1. Unicode的一个翻译逸出(第3.3节)的原始流中的Unicode字符转换为相应的Unicode字符。形式为\ uxxxx的Unicode转义符(其中xxxx是十六进制值)表示编码为xxxx的UTF-16编码单元。该翻译步骤允许任何程序仅使用ASCII字符表示。
+2

重要的是,在字节码发射之前它是* long * - 它是在解析完成之前的任何其他部分。 –

+1

@JonSkeet感谢您的信息。也许我应该使用术语词法分析器而不是解析器:) – manouti

+0

@ AR.3感谢您的回答,两个答案都很棒,我有最难接受的一个选择。无论如何,我会接受约翰的答案,再次感谢。我希望我可以同时接受:) –

0

这仅仅是有趣的是,以下的作品(从参考取)

System.out.println("a\".length() + \"b".length()); 

但下面产生一个编译错误

System.out.println("a\\\u0022.length() + \\\u0022b".length()); 

在第二个,编译器应该减少\",把它们放在一起作为\",但它试过了,它不编译("仍然关闭字符串)。