2013-03-19 81 views
4

我有下面這段代碼Java字符串的字符編碼 - 對於法國 - 荷蘭的語言環境

public static void main(String[] args) throws UnsupportedEncodingException { 
     System.out.println(Charset.defaultCharset().toString()); 

     String accentedE = "é"; 

     String utf8 = new String(accentedE.getBytes("utf-8"), Charset.forName("UTF-8")); 
     System.out.println(utf8); 
     utf8 = new String(accentedE.getBytes(), Charset.forName("UTF-8")); 
     System.out.println(utf8); 
     utf8 = new String(accentedE.getBytes("utf-8")); 
     System.out.println(utf8); 
     utf8 = new String(accentedE.getBytes()); 
     System.out.println(utf8); 
} 

上述輸出如下

windows-1252 
é 
? 
é 
é 

有人可以幫助我瞭解這是否做什麼?爲什麼這個輸出?

+0

爲了獲得預期輸出,請確保爲該文件設置文件編碼類型'UTF8'。如果您使用的是eclipse,請右鍵單擊文件選擇屬性,然後選擇utf8作爲文本文件編碼類型。 – user964147 2013-03-19 13:21:34

回答

6

如果您已有String,則無需對其進行編碼和解碼,該字符串已經是某人解碼原始字節的結果。

在字符串文字的情況下,某人是編譯器將您的源代碼讀取爲原始字節並將其解碼爲您指定的編碼。如果您已經使用Windows-1252編碼實際保存了源文件,並且編譯器將其解碼爲Windows-1252,則一切正常。如果沒有,你需要通過聲明正確的編碼,編譯器編譯源時使用,以解決這個問題...

String utf8 = new String(accentedE.getBytes("utf-8"), Charset.forName("UTF-8")); 

絕對沒有。 (編碼爲UTF-8,解碼爲UTF-8 ==無操作)

utf8 = new String(accentedE.getBytes(), Charset.forName("UTF-8")); 

串進行編碼作爲視窗-1252,然後將其解碼爲UTF-8。結果只能在Windows-1252中解碼(因爲它是在Windows-1252中編碼的,duh),否則你會得到奇怪的結果。

utf8 = new String(accentedE.getBytes("utf-8")); 

字符串編碼爲UTF-8,然後將其解碼與Windows 1252。相同的原則適用於以前的情況。

utf8 = new String(accentedE.getBytes()); 

絕對沒有。 (編碼與Windows 1252,解碼與Windows 1252 ==無操作)

比喻用整數可能更容易理解:

int a = 555; 
//The case of encoding as X and decoding right back as X 
a = Integer.parseInt(String.valueOf(a), 10); 
//a is still 555 

int b = 555; 
//The case of encoding as X and decoding right back as Y 
b = Integer.parseInt(String.valueOf(b), 15); 
//b is now 1205 I.E. strange result 

這兩者都是無用的,因爲我們已經擁有了我們需要做任何的代碼之前,整數555

有必要對 編碼的字符串轉換成原始字節,當它會使系統和有需要的原始字節解碼成字符串時,他們進入你的系統。沒有必要在系統內對進行編碼和解碼。

+0

「(編碼爲Windows-1252,解碼爲Windows-1252 == no-op)」 - 不正確。它將破壞Windows 1252中不可用的所有字符,並將它們變成問號。 – 2014-09-06 21:11:31

+0

@KarolS你正在脫離上下文聽起來聰明大聲笑 – Esailija 2014-09-09 08:51:18

0

當你呼籲字符串getBytes方法是:

將此String解碼使用平臺默認的字符集的字節序列,並將結果存儲到一個新的字節數組。

所以每當你做的事:

accentedE.getBytes() 

需要accentedE String的內容作爲默認的操作系統代碼頁字節encoded,你的情況cp-1252

這條線:

new String(accentedE.getBytes(), Charset.forName("UTF-8")) 

取accentedE字節(CP1252編碼),並試圖將它們以UTF-8進行解碼,因此,該錯誤。從另一面同樣的情況爲:

new String(accentedE.getBytes("utf-8")) 

的getBytes方法發生在CP-1252編碼的accentedE字節,再編碼它們以UTF-8但隨後的字符串constructor與默認OS代碼頁是CP對它們進行編碼-1252。

通過使用平臺的默認字符集解碼指定的字節數組構造新的字符串。新字符串的長度是字符集的函數,因此可能不等於字節數組的長度。

我強烈建議閱讀這篇精彩文章:

The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)

UPDATE:

總之,每一個字符存儲爲一個數字。爲了知道哪個字符是OS使用代碼頁的哪個數字。考慮下面的代碼片斷:

String accentedE = "é"; 

System.out.println(String.format("%02X ", accentedE.getBytes("UTF-8")[0])); 
System.out.println(String.format("%02X ", accentedE.getBytes("UTF-8")[1])); 
System.out.println(String.format("%02X ", accentedE.getBytes("windows-1252")[0])); 

,其輸出:

C3 
A9 
E9 

這是因爲在UTF-8小重音符號e爲存儲爲值C3A9兩個字節,而在cp-1252存儲爲值的單個字節E9。有關詳細解釋,請閱讀鏈接的文章。

1

行#1 - 系統上的默認字符集是windows-1252。

第2行 - 通過將字符串文字編碼爲UTF-8字節,然後使用UTF-8方案對其進行解碼來創建字符串。結果正確地形成了字符串,可以使用windows-1252編碼正確輸出。

第3行 - 通過將字符串文字編碼爲windows-1252,然後使用UTF-8對其進行解碼來創建字符串。 UTF-8解碼器檢測到一個不可能是UTF-8的序列,並用問號「?」取代了違規字符。 (UTF-8格式表示最高位設置爲1的任何字節都是多字節字符的一個字節,但windows-1252編碼只有一個字節長。這是不好的UTF-8)

4號線 - 你通過UTF-8編碼創建了一個字符串,然後在windows-1252中解碼。在這種情況下解碼沒有「失敗」,但它產生了垃圾(又名mojibake)。輸出2個字符的原因是「é」的UTF-8編碼是2個字節的序列。

第5行 - 您通過編碼創建了一個字符串爲windows-1252並解碼爲windows-1252。這產生了正確的輸出。


佔總體的教訓是,如果你編碼字符一個字符編碼字節,然後用編碼你是容易獲得某種形式的重整不同的字符進行解碼。