【转载】计算机程序的思惟逻辑 (7) - 如何从乱码中恢复 (下)?

乱码html

上节说到乱码出现的主要缘由,即在进行编码转换的时候,若是将原来的编码识别错了,并进行了转换,就会发生乱码,并且这时候不管怎么切换查看编码的方式,都是不行的。编程

咱们来看一个这种错误转换后的乱码,仍是用上节的例子,二进制是(16进制表示):C3 80 C3 8F C3 82 C3 AD,不管按哪一种编码解析看上去都是乱码:swift

UTF-8 ÀÏÂí
Windows-1252 ÀÏÂí
GB18030 脌脧脗铆
Big5 ���穩

虽然有这么多形式,但咱们看到的乱码形式极可能是"ÀÏÂí",由于在例子中UTF-8是编码转换的目标编码格式,既然转换为了UTF-8,通常也是要按UTF-8查看。windows

乱码恢复数组

"乱"主要是由于发生了一次错误的编码转换,恢复是要恢复两个关键信息,一个是原来的二进制编码方式A,另外一个是错误解读的编码方式B。编辑器

恢复的基本思路是尝试进行逆向操做,假定按一种编码转换方式B获取乱码的二进制格式,而后再假定一种编码解读方式A解读这个二进制,查看其看上去的形式,这个要尝试多种编码,若是能找到看着正常的字符形式,那应该就能够恢复。测试

这个听上去可能比较模糊,咱们举个例子来讲明,假定乱码形式是"ÀÏÂí",尝试多种B和A来看字符形式。咱们先使用编辑器,以UltraEdit为例,而后使用Java编程来看。编码

使用UltraEditspa

UltraEdit支持编码转换和切换查看编码方式,也支持文件的二进制显示和编辑,因此咱们以UltraEdit为例,其余一些编辑器可能也有相似功能。code

新建一个UTF-8编码的文件,拷贝"ÀÏÂí"到文件中。使用编码转换,转换到windows-1252编码,功能在 "文件"->"转换到"->"西欧"->WIN-1252。
转换完后,打开十六进制编辑,查看其二进制形式,以下图所示:

 

能够看出,其形式仍是ÀÏÂí,但二进制格式变成了 C0 CF C2 ED。这个过程,至关于假设B是windows-1252。这个时候,再按照多种编码格式查看这个二进制,在UltraEdit中,关闭十六进制编辑,切换查看编码方式为GB18030,功能在 "视图"->"查看方式(文件编码)"->"东亚语言"->GB18030,切换完后,一样的二进制神奇的变为了正确的字符形式 "老马",打开十六进制编辑器,能够看出,二进制仍是C0 CF C2 ED,这个GB18030至关于假设A是GB18030。

这个例子咱们碰巧第一次就猜对了。实际中,咱们可能要作屡次尝试,过程是相似的,先进行编码转换(使用B编码),而后使用不一样编码方式查看(使用A编码),若是能找到看上去对的形式,就恢复了。下图列出了主要的B编码格式,对应的二进制,按A编码解读的各类形式。

能够看出,第一行是正确的,也就是说原来的编码实际上是A即GB18030,但被错误解读成了B即Windows-1252了。

使用Java

关于使用Java咱们还有不少知识没有介绍,但一些读者已经有很好的Java知识,因此本文一并列出相关代码,初学者不明白的咱们随后会进一步讲解。

Java中处理字符串的类有String,String中有咱们须要的两个重要方法:

  • public byte[] getBytes(String charsetName),这个方法能够获取一个字符串的给定编码格式的二进制形式
  • public String(byte bytes[], String charsetName),这个构造方法以给定的二进制数组bytes按照编码格式charsetName解读为一个字符串。

将A看作GB18030,B看作Windows-1252,进行恢复的Java代码以下所示:

String str = "ÀÏÂí";
String newStr = new String(str.getBytes("windows-1252"),"GB18030");
System.out.println(newStr);

先按照B编码(windows-1252)获取字符串的二进制,而后按A编码(GB18030)解读这个二进制,获得一个新的字符串,而后输出这个字符串的形式,输出为"老马"。

一样,这个一次碰巧就对了,实际中,咱们能够写一个循环,测试不一样的A/B编码中的结果形式,代码以下所示:

复制代码
public static void recover(String str) 
        throws UnsupportedEncodingException{
    String[] charsets = new String[]{"windows-1252","GB18030","Big5","UTF-8"};
    for(int i=0;i<charsets.length;i++){
        for(int j=0;j<charsets.length;j++){
            if(i!=j){
                String s = new String(str.getBytes(charsets[i]),charsets[j]);
                System.out.println("---- 原来编码(A)假设是: "+charsets[j]+", 被错误解读为了(B): "+charsets[i]);
                System.out.println(s);
                System.out.println();    
            }
        }
    }
} 
复制代码

以上代码使用不一样的编码格式进行测试,若是输出有正确的,那么就能够恢复。

恢复的讨论

能够看出,这种尝试须要进行不少次,上面例子尝试了常见编码GB18030/Windows 1252/Big5/UTF-8共十二种组合。这四种编码是常见编码,在大部分实际应用中应该够了,但若是你的状况有其余编码,能够增长一些尝试。

不是全部的乱码形式都是能够恢复的,若是形式中有不少不能识别的字符如�?,则很难恢复,另外,若是乱码是因为进行了屡次解析和转换错误形成的,也很难恢复。

小结

上节和本节介绍了编码的知识,乱码的缘由及恢复方法,这些都是与语言无关的。

接下来,是时候看看在Java中如何表示和处理字符了,咱们知道Java中用char类型表示一个字符,但在第三节咱们提到了一个问题,即"字符类型怎么也能够进行算术运算和比较?"。

咱们须要对Java中的字符类型有一个更为清晰和深入的理解。

相关文章
相关标签/搜索