Java 正確的做字串編碼轉換

AlbenXie發表於2018-08-09

Java 正確的做字串編碼轉換
字串的內部表示?
字串在java中統一用unicode表示( 即utf-16 LE) 


對於 String s = "你好哦!";
如果原始碼檔案是GBK編碼, 作業系統(windows)預設的環境編碼為GBK,那麼編譯時,  JVM將 按照GBK編碼將位元組陣列解析成字元,然後將字元轉換為unicode格式的位元組陣列,作為內部儲存。
當列印這個字串時,JVM 根據作業系統本地的語言環境,將unicode轉換為GBK,然後作業系統將GBK格式的內容顯示出來。
 
當原始碼檔案是UTF-8, 我們需要通知編譯器原始碼的格式,javac -encoding utf-8 ... , 編譯時,JVM按照utf-8 解析成字元,然後轉換為unicode格式的位元組陣列, 那麼不論原始碼檔案是什麼格式,同樣的字串,最後得到的unicode位元組陣列是完全一致的,顯示的時候,也是轉成GBK來顯示(跟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檔案中讀取出來的, 轉換為string 變成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)
可以幫助我們輕鬆的按照指定編碼讀寫檔案。

相關文章