Android中文亂碼的分析與解決

    在手機與PC之間進行socket或HTTP通訊時,常常會出現中文亂碼問題,其主要原因在於手機端字符編碼默認爲UTF-8,而服務器PC端默認爲gbk或gb2312。

    手機到PC的信息編碼傳遞、接收、解碼顯示的過程爲:手機端以UTF-8編碼信息(包括漢字),寫入socket輸出流。在寫入輸出流,或在socket中傳遞時,或在PC端讀取來自手機的Socket流信息時,系統把信息編碼進行了一次轉換(經編程驗證應該是轉爲GBK),這樣在PC端默認以GBK方式解碼顯示來自手機的信息,出現中文亂碼也就不足爲奇了。

    根據以上分析,要解決PC端顯示中文亂碼的問題,必須在PC端對接受的手機端信息進行轉碼,具體過程如下:

    1、手機端:

    手機端一般可保持默認操作方式即可。當然,執行下述操作也不會產生錯誤輸出:

    strSend = new String(strSend.getBytes(), "UTF-8");

    該操作是按照(手機)當前操作系統的編碼格式獲取字符串strSend的編碼字節(數組),再轉換爲UTF-8字符串,進行傳遞。由於手機端默認使用UTF-8編碼,所以該操作實際上是多餘的。當然,你也可以將操作中第二個參數改變,轉換其它編碼格式傳遞試一下。

    2、PC端:

    tempstr = new String(mStrMSG.getBytes("GBK"),"UTF-8");或者:

    tempstr = new String(mStrMSG.getBytes(),"UTF-8");

    PC端的操作就是以默認編碼方式(這裏爲GBK)獲取接收到字符串編碼字節,再轉換爲UTF-8格式的字符串顯示,以呼應手機端UTF-8的編碼格式。

    在本例所使用的設備環境下,只有轉換爲UTF-8纔可以正確顯示中文,轉爲其它格式都會出現亂碼。其原因應該是手機端編碼爲UTF-8,PC端則須相應以UTF-8解碼。

    這裏兩行語句的不同之處僅在於getBytes()是否帶參數,不帶參數表示以當前默認編碼格式獲取字節數組,帶參數表示以給出的特定編碼格式獲取字符串的字節數組。 

    另外,經實驗,PC端所接收的手機端發送來的中文信息,再次傳回手機模擬器時不需要轉換編碼,否則手機端可能會顯示亂碼。這就出現了下面有趣的操作:

    mPrintWriter.println(mStrMSG + new String("  測試".getBytes("UTF-8")));

    其中mStrMSG爲從手機端獲取的包含漢字的信息,發回手機時不需要轉換,而附加的漢字信息"  測試"則需要執行轉碼操作。而且只能以new String("  測試".getBytes("UTF-8"))的方式獲取UTF-8編碼字節數組,並生成UTF-8字符串;如果換成new String("  測試".getBytes(),"UTF-8")語句,則還是亂碼。

    以上解決方案的源碼下載

 

    從以上分析可以看出,UTF-8和GBK等之間的編碼、解碼、轉換等操作,需要非常小心,否則會出現很多意想不到的錯誤。而且,上述解決方案也並非完善,如果漢字字數爲奇數時,最後一個漢字還會是亂碼,或"??"。

    究其原因,主要是gbk的漢字編碼爲2字節;而UTF-8的漢字編碼則爲3字節。
    爲了解決奇數個漢字末尾的亂碼問題,需要採用從socket輸入流直接讀取字節再轉碼的方式:

    以下是PC端的部分代碼:

    ......

    private InputStream inputStream;

    ......

    while (true) {
       //inputStream.available()爲輸入流中的可讀字符個數
       while (inputStream.available() > 0) {
          strBytes = new byte[inputStream.available()];
          inputStream.read(strBytes);
          mStrMSG = new String(strBytes,"utf-8");
          if (mStrMSG.trim().equals("exit")) {
             // 當一個客戶端退出時
             mClientList.remove(mSocket);
             mPrintWriter.close();
             mStrMSG = "user:" + this.mSocket.getInetAddress()
                 + " exit(total:" + mClientList.size()+")";
             mSocket.close();
             sendMessage();
             //退出當前線程的運行
             return;
          } else {
             mStrMSG = mSocket.getInetAddress() + ":" + mStrMSG;
             sendMessage();
        }
      }

      ......

      手機模擬器端與此類似。

      完整代碼下載

 

參考文章:

    《android應用開發揭祕》

     關於Android與pc通信時中文亂碼的分析和解決

     Android http中文亂碼問題總結

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