天天看點

java中文亂碼解決之道(五)—–java是如何編碼解碼的

編碼&解碼

1:i/o操作

2:記憶體

3:資料庫

4:javaweb

下面主要介紹前面兩種場景,資料庫部分隻要設定正确編碼格式就不會有什麼問題,javaweb場景過多需要了解url、get、post的編碼,servlet的解碼,是以javaweb場景下節lz介紹。

i/o操作

在前面lz就提過亂碼問題無非就是轉碼過程中編碼格式的不統一産生的,比如編碼時采用utf-8,解碼采用gbk,但最根本的原因是字元到位元組或者位元組到字元的轉換出問題了,而這中情況的轉換最主要的場景就是i/o操作的時候。當然i/o操作主要包括網絡i/o(也就是javaweb)和磁盤i/o。網絡i/o下節介紹。

首先我們先看i/o的編碼操作。

java中文亂碼解決之道(五)—–java是如何編碼解碼的

inputstream為位元組輸入流的所有類的超類,reader為讀取字元流的抽象類。java讀取檔案的方式分為按位元組流讀取和按字元流讀取,其中inputstream、reader是這兩種讀取方式的超類。

按位元組

我們一般都是使用inputstream.read()方法在資料流中讀取位元組(read()每次都隻讀取一個位元組,效率非常慢,我們一般都是使用read(byte[])),然後儲存在一個byte[]數組中,最後轉換為string。在我們讀取檔案時,讀取位元組的編碼取決于檔案所使用的編碼格式,而在轉換為string過程中也會涉及到編碼的問題,如果兩者之間的編碼格式不同可能會出現問題。例如存在一個問題test.txt編碼格式為utf-8,那麼通過位元組流讀取檔案時所獲得的資料流編碼格式就是utf-8,而我們在轉化成string過程中如果不指定編碼格式,則預設使用系統編碼格式(gbk)來解碼操作,由于兩者編碼格式不一緻,那麼在構造string過程肯定會産生亂碼,如下:

輸出結果:锘挎垜鏄?cm

test.txt中的内容為:我是 cm。

要想不出現亂碼,在構造string過程中指定編碼格式,使得編碼解碼時兩者編碼格式保持一緻即可:

按字元

其實字元流可以看做是一種包裝流,它的底層還是采用位元組流來讀取位元組,然後它使用指定的編碼方式将讀取位元組解碼為字元。在java中reader是讀取字元流的超類。是以從底層上來看按位元組讀取檔案和按字元讀取沒什麼差別。在讀取的時候字元讀取每次是讀取留個位元組,位元組流每次讀取一個位元組。

位元組&字元轉換

位元組轉換為字元一定少不了inputstreamreader。api解釋如下:inputstreamreader 是位元組流通向字元流的橋梁:它使用指定的 charset 讀取位元組并将其解碼為字元。它使用的字元集可以由名稱指定或顯式給定,或者可以接受平台預設的字元集。 每次調用 inputstreamreader 中的一個 read() 方法都會導緻從底層輸入流讀取一個或多個位元組。要啟用從位元組到字元的有效轉換,可以提前從底層流讀取更多的位元組,使其超過滿足目前讀取操作所需的位元組。api解釋非常清楚,inputstreamreader在底層讀取檔案時仍然采用位元組讀取,讀取位元組後它需要根據一個指定的編碼格式來解析為字元,如果沒有指定編碼格式則采用系統預設編碼格式。

記憶體

首先我們看下面這段簡單的代碼

在這段代碼中我們看到了三處編碼轉換過程(一次編碼,兩次解碼)。先看string.gettytes():

内部調用stringcoding.encode()方法操作:

encode(char[] paramarrayofchar, int paramint1, int paramint2)方法首先調用系統的預設編碼格式,如果沒有指定編碼格式則預設使用iso-8859-1編碼格式進行編碼操作,進一步深入如下:

同樣的方法可以看到new string 的構造函數内部是調用stringcoding.decode()方法:

decode方法和encode對編碼格式的處理是一樣的。

對于以上兩種情況我們隻需要設定統一的編碼格式一般都不會産生亂碼問題。

編碼&編碼格式

首先先看看java編碼類圖

java中文亂碼解決之道(五)—–java是如何編碼解碼的

首先根據指定的chart設定chartset類,然後根據chartset建立chartsetencoder對象,最後再調用 charsetencoder.encode 對字元串進行編碼,不同的編碼類型都會對應到一個類中,實際的編碼過程是在這些類中完成的。下面時序圖展示詳細的編碼過程:

java中文亂碼解決之道(五)—–java是如何編碼解碼的

通過這編碼的類圖和時序圖可以了解編碼的詳細過程。下面将通過一段簡單的代碼對iso-8859-1、gbk、utf-8編碼

通過程式我們可以看到“我是 cm”的結果為:

char[]:6211 662f 20 63 6d

iso-8859-1:3f 3f 20 63 6d

gbk:ce d2 ca c7 20 63 6d

utf-8:e6 88 91 e6 98 af 20 63 6d

圖如下:

java中文亂碼解決之道(五)—–java是如何編碼解碼的

<a href="http://cmsblogs.com/?p=1491">原文連結</a>