2016-03-09 62 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()); 

在第二個,編譯器應該減少\",把它們放在一起作爲\",但它試過了,它不編譯("仍然關閉字符串)。