【转】中文乱码恢复
轉載自:中文亂碼恢復
中文亂碼是開發中常見的問題,一般情況下出現中文亂碼是因為對 中文字符 的編碼方式和解密方式不一致導致的,這種情況下,只要設置統一的字符編碼方式就可以解決或者避免出現亂碼問題。但在項目開發中,偶爾會出現不管怎么設置編碼方式,都不能正確恢復亂碼的問題。上次正好遇到了這樣一個中文亂碼問題,在解決問題的過程中,了解了字符編碼的歷史,常見編碼字符集的由來,對解決中文亂碼問題起到了很大的幫助(對字符編碼以及各種常見字符集的分析總結,會在后面的博客中發出來)。
對于切換編碼方式無法解決的中文亂碼問題,常見的原因是:一段使用A編碼方式的中文,在傳輸、存儲的過程中被錯誤的使用B編碼方式編碼,再次展示的時候使用A編碼方式解碼,最終出現了很奇怪的亂碼問題。
例如: 你好 這個中文單詞,最開始使用 UTF-8 編碼,那么其16進制編碼為:E4BDA0 E5A5BD,然而在解碼的時候,錯誤的使用了 GB18030 編碼方式,也就是說,這個時候解碼程序以GB18030字符集解碼E4BD A0E5 A5BD 這段16進制,得到的結果是:浣犲ソ 。很明顯,這個時候亂碼了。這時,在傳輸、存儲這段亂碼文本的時候,使用了 UTF-8 編碼方式來進行編碼,也就是對 浣犲ソ 這段文本進行編碼,得到16進制編碼為:E6B5A3E78AB2E382BD20 ,然后數據庫或文件里存儲的內容就變成了E6B5A3E78AB2E382BD20。
對于E6B5A3E78AB2E382BD20 這段16進制代碼,如果使用 UTF-8 編碼方式來解碼的話,得到的是: 浣犲ソ ,亂碼了。而如果使用 GB18030字符集 來解碼的話,得到的是:嫻g姴銈?? 這樣的亂碼。不管使用其他的各種編碼字符集進行解碼,得到的結果都是一樣——各種各樣的亂碼。
從亂碼的過程可以看出,關鍵的一步在于第一次解碼的時候,也就是使用 GB18030字符集解碼的時候,亂碼了,后面就是按照亂碼文本進行存儲,就算是用存儲時的字符集解碼,得到的依然是亂碼文本。
這個時候怎么辦呢?瞎猜。
嘗試各種常見的字符集,先推測最開始用什么字符集編碼的,后來又用的什么字符集解碼的,對各種可能都嘗試一遍,看哪種組合的結果能推導出正確的中文文本,就可以知道如果恢復了。
自己寫了一個恢復中文亂碼的程序,成功的恢復了亂碼的中文文本,并在后續的亂碼問題修復中多次使用,貼出來供大家參考一下。
1 package com.zy.test.encode;
2
3 import java.io.UnsupportedEncodingException;
4
5 public class EncodeTest {
6
7 private static String[] charsetArr = {"UTF-8","GB18030","GB2312","GBK","Windows-1252","ISO8859-1"};
8
9 public static void testAllCharset(String text) throws UnsupportedEncodingException {
10 if (text == null || text.length() == 0) {
11 System.out.println("文本不能為空");
12 return;
13 }
14
15 System.out.println("假設當前編碼 假設原始編碼 編碼后的內容");
16 printSeparator();
17
18 for (String curCharset : charsetArr) {
19 byte[] btArr = text.getBytes(curCharset);
20 for (String originCharset : charsetArr) {
21 if (originCharset.equals(curCharset)) {
22 continue;
23 }
24 String encodeText = new String(btArr,originCharset);
25 printTable(curCharset, originCharset, encodeText);
26 }
27 printSeparator();
28 }
29 }
30
31 private static void printSeparator() {
32 System.out.println("--------------------------------------------------------");
33 }
34
35 private static void printTable(String curCharset, String originCharset, String encodeText) {
36 System.out.print(curCharset);
37 for (int i = 0; i < 15 - curCharset.length(); i++) {
38 System.out.print(" ");
39 }
40 System.out.print("| " + originCharset);
41 for (int i = 0; i < 16 - originCharset.length(); i++) {
42 System.out.print(" ");
43 }
44 System.out.println("| " + encodeText);
45 }
46
47 public static void main(String[] args) throws UnsupportedEncodingException {
48 //測試亂碼
49 testAllCharset("浣犲ソ");
50 }
51 }
執行結果如下:
從執行結果可以看出,只有在 假設當前編碼 為 ISO9959-1 ,假設原始編碼 為UTF-8 時,正確的恢復了亂碼的中文文本。由此可以得出,該亂碼最開始使用 UTF-8 編碼,之后錯誤的使用了 ISO8859-1 字符集進行解碼,并按照錯誤解碼得到的二進制進行存儲、傳輸(實際上大多數的中文亂碼場景都是這樣)。
需要注意的是,這個亂碼恢復代碼僅適用于 被錯誤解碼一次 的情況,如果有多次被錯誤解碼,那么恢復起來就需要嘗試更多可能的字符集組合。另外,如果亂碼后的文本中出現 ?? 這樣的情況,則有可能無法恢復,因為亂碼之前的信息已經丟失了,例如使用UTF-8編碼,然后使用GB18030解碼,因為UTF-8的編碼范圍遠大于GB18030的編碼范圍,所以解碼的時候,一些在GB18030字符集中找不到的編碼就會丟失掉了。
另外,推薦一個亂碼恢復的網站,http://www.mytju.com/classcode/tools/messycoderecover.asp 可以對亂碼進行一定的恢復,原理跟我上面說的一樣(實際使用中發現有時亂碼無法恢復,但是使用上面的程序就可以)。
正確的恢復了亂碼的中文,找到了最開始的編碼方式和錯誤解碼時的編碼方式,那該如何在程序中解決這個問題呢?
一般情況下,在錯誤解碼的地方設置使用正確的編碼字符集就可以解決問題。所以避免中文亂碼的原則就是使用統一的字符集,所有的地方都統一使用某個字符集。
如果沒法設置字符集,或者設置了正確的字符集但沒有生效而又找不到解決方案的情況下,可以手動的對亂碼文本進行轉碼(不推薦這種方式)。
例如,在上面測試的程序中,最原始的編碼是UTF-8,錯誤解碼使用的是ISO8859-1,那么可以用下面的方式進行轉碼恢復:
String newStr = new String(luanma.getBytes("ISO8859-1"),"UTF-8");
以上就是對處理這次中文亂碼問題的一個小總結,希望能對別人有所幫助!
總結
- 上一篇: udhcpc 参数使用说明
- 下一篇: 丫蛋小品我要上春晚(丫蛋老公)