Java編碼理解關鍵字:Java編碼,Unicode, getBytes()來源於我的筆記:有道筆記-Java編碼理解各類程式涉及的編碼不外乎這些: 原始碼檔案的編碼, 程式中字串的編碼, 程式執行輸出顯示時的編碼.Java號稱國際化的語言,是因為它的class檔案採用UTF-8,而JVM運行時使用UTF-16。摘自《談談對Java中Unicode、編碼的理解 http://blog.csdn.net/soleghost/article/details/959832 》在java程式的編譯和運行過程中都涉及了編碼轉換.
1. JAVAC是以系統預設編碼讀入源檔案,然後產生按UNICODE編碼的CLASS檔案。 摘自《Java GetBytes編碼方式 http://nopainnogain.iteye.com/blog/970628 》
windows 系統預設編碼一般是 GBK,Linux 可以通過 LANG 來設定.
可以通過指定編碼方式改變Javac讀入源檔案的編碼方式。
javac -encoding GBK Test.java
若一個來源程式檔案的編碼是 GBK, 並且程式中涉及中文字串,(手動輸入的中文字元的編碼方式就是當前檔案的編碼方式GBK. )
此時,若 LANG=UTF-8,使用 javac Test3.java 編譯器時會出現如下警告:
這是因為 javac 按照系統預設編碼 UTF-8 來讀取檔案, 遇到裡面的實為GBK中文字元無法在 UTF-8字元集中找到相應字元,所以出現警告。
而使用 javac -encoding GBK Test3.java 則不會出現警告
2. 程式運行過程中調用getBytes(),new String(),println 列印字串顯示的過程中,都有編碼的轉換過程.
Java 中的字串都是按 unicode 方式儲存的. 所有的編碼轉換均是從原始編碼轉化為 unicode,再由 unicode 轉換為 目標編碼的過程。
如下程式:
若 getBytes(),new String() 不指定相關編碼, 則它會按照系統預設編碼來處理
例如 gbkStr.getBytes() 會按照系統預設編碼來返回位元組流, new String(bytes)會認為 bytes 位元組流是系統預設編碼。
上述程式中的 gbkStr.getBytes().length 和 utfStr.getBytes().length 會在運行時由於 LANG 不同而出現不同的結果。
LANG = zh_CN.GB18030,終端編碼為 GBK,運行結果為:
LANG = zh_CN.UTF-8,終端編碼為 UTF-8,運行結果為:
從運行結果看,我們有個疑問,為什麼第一個字串列印出來顯示都正常,而第二個字串卻顯示不正常呢。
我們看看這行代碼 new String(gbkStr.getBytes("GBK"), "UTF-8)
這句代碼可能我們會理解為將 GBK 的 String 轉化為 UTF-8 的String,這是大錯特錯的,實際上這行代碼有嚴重錯誤,直接導致 utfStr 裡儲存的不是能正常解碼的字元。
我們反覆講 JAVA中的String都是unicode編碼的,所以不存在GBK編碼的String”或“UTF8編碼的String”這樣的幼稚說法,上述程式的變數定義
gbkStr 和 utfStr 也是幼稚行為。
而這句話的實際含義是將gbkStr對象按照GBK的方式編碼為byte[],然後再把 byte[] 按照 UTF-8 的方式儲存到String中,(此句摘自《談談對Java中Unicode、編碼的理解 http://blog.csdn.net/soleghost/article/details/959832 》).
這句話涉及的實際操作是:gbkStr(Unicode字元)轉化為GBK字元,gbk字元轉化為 byte[] (即轉化結果存放到 bytes[] 中), 然後是 bytes[] 轉化為 UTF-8 字元, UTF-8 字元轉化為 unicode (即認為 byte[] 中是UTF-8字元的位元組碼,將byte[]轉化為 Unicode).
將一個 gbk 編碼的 byte[] 當作 utf8 方式儲存很明顯會出現錯誤,而正確代碼是這樣。由 gbkStr 對象得到 UTF-8 方式編碼的 byte[], 在按照 UTF-8 方式儲存到 utfStr.
LANG = zh_CN.GB18030,終端編碼為 GBK,運行結果為:
LANG = zh_CN.UTF-8,終端編碼為 UTF-8,運行結果為:
總結結果:
正確代碼
不同環境下運行結果均是:28,36
不同環境下運行結果均是:28,44
注意,
這裡由於原始碼檔案是 GBK 編碼, 所以 是GBK編碼,若檔案是UTF-8編碼,運行結果仍相同,結果與原始碼檔案中字元的編碼毫無關係,這是因為 在賦值給 gbkStr 時已經完成了到 unidoce
的轉化, gbkStr 中儲存的是統一的 unicode (gbkStr.length()得到的實際上是字串中unicode字元數量)。假若是從檔案中讀取字串而不是這裡這種直接語句賦值,則需要我們在讀取檔案時指定檔案的編碼格式才能得到正確的 String。而我們要得到某個編碼的輸出檔案時,只需在將 String 輸出到檔案時指定編碼格式就可以,非常方便。
因此:
要得到一個字串按照 UTF-8 編碼 和 GBK 編碼的位元組數組, 直接使用 string.getBytes(encoding) 就可以了,無需經過複雜的轉化。
上述程式在不同的 LANG和終端環境下均列印顯示正常,是因為在輸出到終端時有一個 unidoce ==> 其它編碼格式的轉化。
由此可看出,java中將字串使用 unidoce 統一編碼十分方便(Python也是這樣),而不像 C/C++ 中必須保證字串的編碼和LANG,終端環境一致才列印顯示正常。
建議在使用 getBytes 和 new String 時均指定編碼。
在上述代碼中, gbkStr 和 utfStr 中儲存的內容(unicode)是一樣的, 它們都對應著 28 個字元, 一個英文或中文均只算一個字元. 我們可以查看一下內容:
Unicode 值
此外,我們也可以看一下 gbk 位元組流 和 utf-8 位元組流的內容。
GBK 位元組流 (GBK中文字元按照兩個位元組編碼,英文按照一個位元組編碼)
UTF-8 位元組流(UTF-8中文字元編碼方式是變長的,這裡的幾個中文字元是按3個位元組編碼)