天天看點

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.2.4 字元編碼方式

2.2.4 字元編碼方式

輸入和輸出流都是用于位元組序列的,但是在許多情況下,我們希望操作的是文本,即字元序列。于是,字元如何編碼成位元組就成了問題。

Java針對字元使用的是Unicode标準。每個字元或“編碼點”都具有一個21位的整數。有多種不同的字元編碼方式,也就是說,将這些21位數字包裝成位元組的方法有多種。

最常見的編碼方式是UTF-8,它會将每個Unicode編碼點編碼為1到4個位元組的序列(請參閱表2-1)。UTF-8的好處是傳統的包含了英語中用到的所有字元的ASCII字元集中的每個字元都隻會占用一個位元組。

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.2.4 字元編碼方式

另一種常見的編碼方式是UTF-16,它會将每個Unicode編碼點編碼為1個或2個16位值(請參閱表2-2)。這是一種在Java字元串中使用的編碼方式。實際上,有兩種形式的UTF-16,被稱為“高位優先”和“低位優先”。考慮一下16位值0x2122。在高位優先格式中,高位位元組會先出現:0x21後面跟着0x22。但是在低位優先格式中,是另外一種排列方式:0x22 0x21。為了表示使用的是哪一種格式,檔案可以以“位元組順序标記”開頭,這個标記為16位數值0xFEFF。讀入器可以使用這個值來确定位元組順序,然後丢棄它。

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.2.4 字元編碼方式

警告:有些程式,包括Microsoft Notepad(微軟記事本)在内,都在UTF-8編碼的檔案開頭處添加了一個位元組順序标記。很明顯,這并不需要,因為在UTF-8中,并不存在位元組順序的問題。但是Unicode标準允許這樣做,甚至認為這是一種好的做法,因為這樣做可以使編碼機制不留疑惑。遺憾的是,Java并沒有這麼做,有關這個問題的缺陷報告最終是以“will not f?ix(不做修正)”關閉的。對你來說,最好的做法是将輸入中發現的所有先導的uFEFF都剝離掉。

除了UTF編碼方式,還有一些編碼方式,它們各自都覆寫了适用于特定使用者人群的字元範圍。例如,ISO 8859-1是一種單位元組編碼,它包含了西歐各種語言中用到的帶有重音符号的字元,而Shift-JIS是一種用于日文字元的可變長編碼。大量的這些編碼方式至今仍在被廣泛使用。

不存在任何可靠的方式可以自動地探測出位元組流中所使用的字元編碼方式。某些API方法讓我們使用“預設字元集”,即計算機的作業系統首選的字元編碼方式。這種字元編碼方式與我們的位元組源中所使用的編碼方式相同嗎?位元組源中的位元組可能來自世界上的其他國家或地區,是以,你應該總是明确指定編碼方式。例如,在編寫網頁時,應該檢查Content-Type頭資訊。

StandardCharsets類具有類型為Charset的靜态變量,用于表示每種Java虛拟機都必須支援的字元編碼方式:

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.2.4 字元編碼方式

為了獲得另一種編碼方式的Charset,可以使用靜态的forName方法:

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.2.4 字元編碼方式

在讀入或寫出文本時,應該使用Charset對象。例如,我們可以像下面這樣将一個位元組數組轉換為字元串:

《Java核心技術 卷Ⅱ 進階特性(原書第10版)》一2.2.4 字元編碼方式

提示:有些方法允許我們用一個Charset對象或字元串來指定字元編碼方式。由于選擇的是StandardCharsets常量,是以無需擔心拼寫錯誤。例如,new String(bytes, "UTF 8") 就不可接受,并且會引發運作時錯誤。