1. 程式人生 > >一次性解決Java程式中的亂碼問題

一次性解決Java程式中的亂碼問題

java在字串中統一用Unicode表示。

對於任意一個字串:String string = “測試字串”;

如果原始檔是GBK編碼,作業系統預設環境編碼也為GBK,那麼編譯的時候,JVM將按照GBK編碼將位元組陣列解析為字元,然後將字元轉換為Unicode格式的位元組陣列,作為內部儲存(位元組陣列→字元→Unicode位元組陣列)

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

當原始碼檔案是UTF-8, 我們需要通知編譯器原始碼的格式,javac -encoding utf-8 … , 編譯時,JVM按照utf-8 解析成字元,然後轉換為unicode格式的位元組陣列, 那麼不論原始碼檔案是什麼格式,同樣的字串,最後得到的unicode位元組陣列是完全一致的,顯示的時候,也是轉成GBK來顯示(跟OS環境有關)

亂碼是如何產生的?

本質上都是由於字串原本的編碼格式與讀取時解析用的編碼格式不一致導致的

亂碼指的是程式顯示出來的字元文字無法用任何語言去解讀。一般情況下會包含大量的?。亂碼問題是所有計算機使用者或多或少會遇到的問題。造成亂碼的原因就是因為使用了錯誤的字元編碼去解碼位元組流,因此當我們在思考任何跟文字顯示有關的問題時,請時刻保持清醒:當前使用的字元編碼是什麼。只有這樣,我們才能正確分析和處理亂碼問題。

例如最常見的網頁亂碼問題。如果你是網站技術人員,遇到這樣的問題,需要檢查以下原因:

伺服器返回的響應頭Content-Type沒有指明字元編碼

網頁內是否使用META HTTP-EQUIV標籤指定了字元編碼

網頁檔案本身儲存時使用的字元編碼和網頁宣告的字元編碼是否一致

java程式碼中的亂碼問題如何解決呢?

例如:String s = “測試字串”;

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

其中getBytes()是將Unicode轉換為作業系統預設格式的位元組陣列,即“測試字串”的GBK格式,new String (bytes, Charset) 中的charset 是指定讀取byte的方式,這裡指定為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)

//利用getBytes將unicode字串轉成UTF-8格式的位元組陣列,然後用utf-8 對這個位元組陣列解碼成新的字串
new String( s.getBytes("utf-8") , "utf-8");

UTF-8 轉GBK原理也是一樣
new String( s.getBytes("GBK") , "GBK");

其實核心工作都由getBytes(charset)做了。getBytes的JDK描述:Encoding 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)

可以幫助我們輕鬆的按照指定編碼讀寫檔案。

附錄:
HttpClient post請求中文亂碼問題解決

最近接到現場同事反饋,在掉介面的過程中,廠家收到的請求報文中文是亂碼的。我檢查了版控的程式碼,找到如下解決辦法:

原始程式碼(中文亂碼):

HttpPost httpPost = new HttpPost(url);
DefaultHttpClient httpClient = new DefaultHttpClient();
//請求頭
httpPost.setHeader("Accept", MediaType.APPLICATION_JSON);
httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
//請求實體
StringEntity reqEntity = new StringEntity(reqStr);
httpPost.setEntity(reqEntity);
//獲取響應
HttpResponse httpResp = httpClient.execute(httpPost);
HttpEntity respEntity = httpResp.getEntity();

解決辦法:

方法一:

//請求實體
HttpEntity reqEntity = new ByteArrayEntity(reqStr.getBytes("UTF-8"));
//StringEntity reqEntity = new StringEntity(reqStr);
httpPost.setEntity(reqEntity);

方法二:

//請求實體
StringEntity reqEntity = new StringEntity(reqStr,Charset.forName("UTF-8"));
httpPost.setEntity(reqEntity);