在手機與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();
}
}
沒有留言:
張貼留言