java 中文亂碼以及轉碼


查看此文章需要對字符集編碼有一定的認識:任意門:字符集編碼基礎

一、字符串的內部表示?

重點:字符串在java(指在JVM中、在內存中)中統一用unicode表示( 即utf-16 LE) , 下面解釋:

對於 String s = "你好哦!";

如果源碼文件(java文件)是GBK編碼, 操作系統(windows)默認的環境編碼為GBK,那么編譯時,  JVM將 按照GBK編碼將字節數組解析成字符(系統文件本質就是二進制流),然后將字符轉換為unicode格式的字節數組,作為內部存儲。

當打印這個字符串時,JVM 根據操作系統本地的語言環境,將unicode轉換為GBK,然后操作系統將GBK格式的內容顯示出來。

 

當源碼文件是UTF-8, 我們需要通知編譯器源碼的格式,javac -encoding utf-8 ... , 編譯時,JVM按照utf-8 解析成字符,然后轉換為unicode格式的字節數組, 所以不論源碼文件是什么格式,同樣的字符串,最后得到的unicode字節數組是完全一致的,顯示的時候,也是轉成GBK來顯示(跟OS環境有關:OS表示操作系統)

 

二、亂碼如何產生? 本質上都是由於 字符串原本的編碼格式 與 讀取時解析用的編碼格式不一致導致的。

例如:

String s = "你好哦!";

System.out.println( new String(s.getBytes(),"UTF-8")); //錯誤,因為getBytes()默認使用GBK編碼, 而解析時使用UTF-8編碼,肯定出錯。

其中 getBytes() 是將unicode 轉換為操作系統默認的格式的字節數組,即"你好哦"的 GBK格式,

new String (bytes, Charset) 中的charset 是指定讀取 bytes 的方式,這里指定為UTF-8,即把bytes的內容當做UTF-8 格式對待。

如下兩種方式都會有正確的結果,因為他們的源內容編碼和解析用的編碼是一致的。

System.out.println( new String(s.getBytes(),"GBK"));

System.out.println( new String(s.getBytes("UTF-8"),"UTF-8"));

 

三、如何利用getBytes 和 new String() 來進行編碼轉換呢?

 網上流傳着一種錯誤的方法:

GBK--> UTF-8:    new String( s.getBytes("GBK") , "UTF-8);   ,這種方式是完全錯誤的,因為getBytes 的編碼與  UTF-8 不一致,肯定是亂碼。

但是為什么在tomcat 下,使用 new String(s.getBytes("iso-8859-1") ,"GBK") 卻可以用呢? 答案是:

tomcat 默認使用iso-8859-1編碼, 也就是說,如果原本字符串是GBK的,tomcat傳輸過程中,將GBK轉成iso-8859-1了,

默認情況下,使用iso-8859-1讀取中文肯定是有問題的,那么我們需要將iso-8859-1 再轉成GBK, 而iso-8859-1 是單字節編碼的,

即他認為一個字節是一個字符, 那么這種轉換不會對原來的字節數組做任何改變,因為字節數組本來就是由單個字節組成的,

如果之前用GBK編碼,那么轉成iso-8859-1后編碼內容完全沒變, 則 s.getBytes("iso-8859-1")  實際上還是原來GBK的編碼內容

則 new String(s.getBytes("iso-8859-1") ,"GBK")  就可以正確解碼了。 所以說這是一種巧合。

 

四、如何正確的將GBK轉UTF-8 ? (實際上是unicode轉UTF-8)

String gbkStr = "你好哦!"; //源碼文件是GBK格式,或者這個字符串是從GBK文件中讀取出來的, JVM會把字符轉化成unicode格式

//利用getBytes將unicode字符串轉成UTF-8格式的字節數組

byte[] utf8Bytes = gbkStr.getBytes("UTF-8"); 

//然后用utf-8 對這個字節數組解碼成新的字符串

String utf8Str = new String(utf8Bytes, "UTF-8");

簡化后就是:

unicodeToUtf8 (String s) {

return new String( s.getBytes("utf-8") , "utf-8");

}

UTF-8 轉GBK原理也是一樣

return new String( s.getBytes("GBK") , "GBK");

 

 其實核心工作都由  getBytes(charset) 做了。

getBytes 的JDK 描述:Encodes this String into a sequence of bytes using the named charset, storing the result into a new byte array.

 

另外對於讀寫文件,

OutputStreamWriter w1 = new OutputStreamWriter(new FileOutputStream("D:\\file1.txt"),"UTF-8");

InputStreamReader( stream, charset)

可以幫助我們輕松的按照指定編碼讀寫文件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM