雖然計算機對英文字符的支持非常不錯,我們也恨不得寫的程序只會處理英文的數據,但是昨為中國人,無可避免地要處理一些中文字符。當很簡單的一件事情,遇到了中文,一切就不同了!本文就會講述實際生產環境中遇到的四個中文迷題,歡迎大家參與補充!
1、“我”講的其它機器聽不懂?
當把一串中文字符,通過HTTP、TCP方式傳遞到另外一個系統時,會經常驚奇地發現,在發送前還是好好的,但是收到後卻全壞了!怎麼辦?下面提供幾種情況,對症下藥:
(1)發送時使用的GET請求
要特別注意傳遞參數時,不要直接使用中文(如?key=熱情),這基本就宣告了這個代碼在某個浏覽器、某個機器上,對方收到的東西就是亂碼了。必須要對傳遞的參數進行Url Encode,接受方再進行Url Decode,取出來的數據基本就是OK的了。注:某些Java框架(如Spring)的RestTemplate會自動對GET方式的調用參與進行Encode、Decode,你就可以不用再人工去做了。
(2)使用了byte傳遞數據
在使用MQ、原生Socket等場景下,有時要用byte傳遞數據。這時一定要對String的getBytes方法傳遞編碼參數,一般用"UTF-8",且接收方則用new String(bytes, "UTF-8")來構造字符串,不然也有亂碼風險!建議對需要用byte傳遞數據的場景,盡量轉為Base64編碼的方式進行傳遞,更方便去調試程序。
(3)設置運行環境編碼
如果你的系統默認編碼未設置對,那麼默認的Java代碼運行環境也不對,所以一要在程序運行時對Java代碼運行環境進行設置。以Linux Shell為例,在啟動應用的shell裡,增加
export LANG=en_US.UTF-8
這可以保證應用在調用系統命令行時,運行環境是以UTF-8編碼的。另外如果是Tomcat,那麼的腳本處也加上參數項:
-Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.language=en -Duser.country=US
這可以保證應用的Java代碼的默認運行是在UTF-8編碼基礎上的,如果需要通信的系統全設置為UTF-8,可以避免諸多編碼轉換!
2、浏覽器不認識“我”?
一般來說,浏覽與應用的交互就是GET、POST請求了,當然還有PUT、DELETE請求,不過觸類旁通,不需要講太多。
POST請求的中文數據,經常是可以正常在前後台傳遞的,但是GET請求就沒那麼幸運了,一定要記得進行URL Encode與Decode,養成好的編碼習慣,減少後續調試代碼的難度與時間。
3、猜一猜“我”說的是什麼?
在讀寫含中文文本的文件時,有時候也會讀出亂碼來,原因是因為程序的運行編碼永遠只有默認的一種的,那麼如果不帶編碼參數地去讀取文件,由於文件編碼可能與程序編碼不同,最後讀出來的就是亂碼了。這個時候,就需要寫程序去“猜”文件的編碼了。
現在有許多開源的識別編碼的類庫可以直接使用,但是也並不全是ok的,常見的編碼能識別就好,像這些:UTF-8、GB2312、GBK、GB18030、UTF-16、US-ASCII、Big5、ISO-8859-1,遇到的概率非常大。我這裡推薦幾個:EncodingDetecotr、jChardet。
在讀取到byte後,通過編碼識別再存為相應的String,就可以得到正常的中文了。
4、聽說UTF-8還有BOM?
BOM,在UCS編碼中有一個叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的編碼是FEFF,這就是所謂的BOM頭了,它的UTF-8編碼是EF BB BF。在Windows系統上,默認用記事本存儲為UTF-8格式,是有BOM的,但是Linux下卻是默認無BOM的。缺了BOM的文件,經常在Windows下就識別出現錯位、亂碼等問題。解決之道就是在讀取文件、byte的前幾個字節,如果無BOM就給它加上BOM。
1 /** 2 * 判斷文件是否有BOM 3 * 4 * @return 5 */ 6 public static boolean hasBom(File file) { 7 FileInputStream input = null; 8 try { 9 input = new FileInputStream(file); 10 byte[] buf = new byte[1024]; 11 if ((input.read(buf, 0, 1024)) != -1) { 12 if (buf[0] == (byte) 0xEF && buf[1] == (byte) 0xBB && buf[2] == (byte) 0xBF) { 13 return true; 14 } 15 } 16 return false; 17 } catch (IOException e) { 18 _logger.error(e); 19 return true; 20 } finally { 21 if (input != null) { 22 try { 23 input.close(); 24 } catch (IOException e) { 25 _logger.error("資源釋放失敗!", e); 26 } 27 } 28 } 29 }
如果無BOM,則在傳給Windows的地方或需要導出的地方這前先加上:new byte[] {(byte) 0xEF, (byte) 0xBB, (byte) 0xBF} 這些字節。
以上這些點如果注意了,常見的中文編碼問題就解決了。