Java的字符集

看本文之前,請先弄清楚什麼是unicode,utf8,utf16。不清楚請移步百度百科http://baike.baidu.com/view/40801.htm

 

Java的String內部有private final char value[] ,使用UTF-16編碼來存儲。也就是說,不管是什麼樣的字符串,只要是存儲在String對象中的,就是UTF-16編碼。

 

那我們讀取的文件的字符集有很多種,怎麼辦呢。String有個構造函數String(byte[] bytes, String charsetName) ,從文件中讀取了bytes後,可以用這個來指定bytes的字符集charset,Java會自動講bytes從charset轉化成UTF-16,然後存到String中。沒有指定的話,那麼使用系統默認的字符集。

 

接下來,還有一個問題是,如果我在源代碼中寫了這樣的語句,如

String test="我是中文"

那麼,是怎麼處理的呢。

 

使用下面的代碼測試:

 

第一次,使用記事本寫,保存。

javac Test.java

java Test

一切正常。

測試效果圖

 

第二次,直接使用記事本,把文件另存爲UTF8。

測試效果圖

測試效果圖

 

 

這個錯誤的原因是,記事本自作聰明地給文件前面多加了三個字節。用UE打開看就能看到了。

 

測試效果圖

 

 

關於這個問題,google一下"記事本 UTF8 BOM"。前人已經寫得很清楚了。

 

OK。我們使用UE將這三個字節刪掉。然後

javac Test.java

java Test

 

測試效果圖

 

可惡的亂碼出現了。

爲什麼會出現亂碼呢?

javac Test.java做了這樣的事,首先javac不知道文件的編碼,因此使用系統默認的字符集(GBK),也就是說javac把UTF8編碼的文件當成了GBK編碼的文件。因此,當javac讀到"我是中文"時,他看到的是這四個字的UTF8編碼的二進制形式,但是他將這些當成GBK編碼了。

 

於是,他就將這些bytes從GBK編碼轉化成UTF16的String對象(這是個錯誤的轉化,因爲得到的就不是"我是中文"的正確的UTF16編碼了)。注意,轉化完後還是可以得到二進制的字節,javac把這些保存在.class文件中。

 

這樣呢,java Test運行時,輸出的就是亂碼了。

 

那麼,如果使用java Test > temp.txt,然後用UE打開temp.txt,會看到什麼呢?

恩,不是亂碼,而是"我是中文"。

 

這又是爲什麼呢?

將"我是中文"的UTf8編碼記爲A

將A用GBK編碼轉化成的UTF16編碼記爲B

這樣,B就是保存在.class中的內容

 

System.out做了什麼事呢?他先將.class中的B從UTF16轉化爲GBK(系統默認編碼)編碼,這樣得到了A。

然後把A傳給控制檯輸出。控制檯使用的編碼也是默認的GBK,因爲顯示出來就是亂碼了。

但是使用重定向後,temp.txt中保存的二進制字節正好是A。

當用UE打開後temp.txt,UE自動根據temp.txt的文件監測編碼,發現是UTF8編碼的二進制字節,因此使用的UTF8來顯示,當然就正常了。(>_<記事本真的很弱)

 

最後,補充一點,對於源碼中的非字符串 部分,javac先按文件的編碼解析,然後將所有遇到的源碼(除引號中的字符串外),都使用UTF8保存在class文件中。有興趣的不妨自己打開class文件看看。

 

下面這個文件我用UTF8 保存:

 

 

 

然後我用javac Test.java,就報錯了。

 

測試效果圖

 

原因是,javac發現裏面有非法的GBK編碼。。。。。。呵呵,經常在Windows寫代碼,然後放到Linux下手動編譯的,可能會覺得這個很熟悉吧。

因爲Windows默認編碼是GBK,Linux默認編碼是UTF。

當你寫一個GBK編碼的Test.java,然後放到Linux下直接javac Test.java,如果文件中有中文的話,一般都會有錯誤。

 

 

建議,如果手動使用javac編譯的話,儘量加上encoding選項,避免不必要的問題出現。

如:javac -encoding utf8 Test.java

其中utf8是Test.java文件的編碼

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章