Java Web開發中經常會遇到中文編碼問題,那麼為什麼需要編碼呢?因為人類需要表示的符號太多,無法用1個字節來表示,而計算機中存儲信息最小單元為1個字節。所以必須指定char與byte之間的編碼規則了。
計算機中提供了多種編碼方式,常見的有ASCII、ISO-8859-1、GBK、GB2312、UTF-16、UTF-8等。
在IO操作中我們一般需要進行編碼,這裡的IO操作包括磁盤IO、網絡IO等,比如下面就是一個磁盤IO的例子:
public static void main(String[] args) throws IOException { String file = "H:/name.txt"; String charset = "utf-8"; // 寫字符到字節流 FileOutputStream outputStream = new FileOutputStream(file); OutputStreamWriter writer = new OutputStreamWriter(outputStream, charset); try { writer.write("中文名杭州"); } finally { writer.close(); } // 讀字節流到字符 FileInputStream inputStream = new FileInputStream(file); InputStreamReader reader = new InputStreamReader(inputStream, charset); try { char[] buffer = new char[64]; reader.read(buffer); System.out.println(buffer); } finally { reader.close(); } }
在程序中設計IO編解碼,只要我們指定統一的編解碼Charset字符集,一般不會有問題(或者編解碼都是在同一個系統中,都使用默認的字符集)。但是如果我們指定的編解碼不一致,就會出現中文亂碼問題。如果把代碼中的InputStreamReader指定為GBK編碼,就會出現亂碼問題。
InputStreamReader reader = new InputStreamReader(inputStream, "GBK");
Java中使用String類型也是可以指定字節到字符轉換的編碼字符集的。
1 String xxx = "你好"; 2 String yyy = new String(xxx.getBytes(), "utf-8"); 3 System.out.println(yyy);
對中文字符GBK/GB2312/UTF-16/UTF-8四種編碼格式都能處理,GB2312 與 GBK 編碼規則類似,但是 GBK 范圍更大,它能處理所有漢字字符,所以 GB2312 與 GBK 比較應該選擇 GBK。UTF-16 與 UTF-8 都是處理 Unicode 編碼,它們的編碼規則不太相同,相對來說 UTF-16 編碼效率最高,字符到字節相互轉換更簡單,進行字符串操作也更好。它適合在本地磁盤和內存之間使用,可以進行字符和字節之間快速切換,如 Java 的內存編碼就是采用 UTF-16 編碼。但是它不適合在網絡之間傳輸,因為網絡傳輸容易損壞字節流,一旦字節流損壞將很難恢復,想比較而言 UTF-8 更適合網絡傳輸,對 ASCII 字符采用單字節存儲,另外單個字符損壞也不會影響後面其它字符,在編碼效率上介於 GBK 和 UTF-16 之間,所以 UTF-8 在編碼效率上和編碼安全性上做了平衡,是理想的中文編碼方式。
當我們碰到一些亂碼時,應該怎麼處理這些問題?出現亂碼問題唯一的原因都是在 char 到 byte 或 byte 到 char 轉換中編碼和解碼的字符集不一致導致的,由於往往一次操作涉及到多次編解碼,所以出現亂碼時很難查找到底是哪個環節出現了問題,下面就幾種常見的現象進行分析。
中文變成了看不懂的字符
例如,字符串“淘!我喜歡!”變成了“Ì Ô £ ¡Î Ò Ï²»¶ £ ¡”編碼過程如下圖所示
字符串在解碼時所用的字符集與編碼字符集不一致導致漢字變成了看不懂的亂碼,而且是一個漢字字符變成兩個亂碼字符。
一個漢字變成一個問號
例如,字符串“淘!我喜歡!”變成了“??????”編碼過程如下圖所示:
將中文和中文符號經過不支持中文的 ISO-8859-1 編碼後,所有字符變成了“?”,這是因為用 ISO-8859-1 進行編解碼時遇到不在碼值范圍內的字符時統一用 3f 表示,這也就是通常所說的“黑洞”,所有 ISO-8859-1 不認識的字符都變成了“?”。
一個漢字變成兩個問號
例如,字符串“淘!我喜歡!”變成了“????????????”編碼過程如下圖所示:
這種情況比較復雜,中文經過多次編碼,但是其中有一次編碼或者解碼不對仍然會出現中文字符變成“?”現象,出現這種情況要仔細查看中間的編碼環節,找出出現編碼錯誤的地方。
參考:
1、深入分析 Java 中的中文編碼問題
2、《深入分析Java Web技術內幕》PDF 下載見 http://www.linuxidc.com/Linux/2016-10/135767.htm