起因

最近遇到了一个XML编码问题:XML声明中的编码方式是GB2312,使用dom4j进行读取后再重新写入新文件,发现新文件中有几个字符变成了乱码,乱码字符是≧​和㎡​。

排查过程

使用项目组提供的demo程序很快就复现了此问题。问题的特殊性在于文件绝大部分是正确的,只有特定的某几个字符乱码。查看二进制发现原先字符对于的二进制分别为≧​(A8 52)和㎡​(A9 4F),乱码对应的二进制分别为3F 52​和3F 4F​。从现象上看像是GB2312不支持这两个字符的编码,为了排除XML解析的干扰,重新写了更简单的demo程序进行测试:

1
2
3
4
5
// 文件内容为有问题的字符
byte[] bs = FileManagerUtil.getContentFromSystem("D:\macd\Desktop\test1.txt");
String s = new String(bs, "GB2312");
System.out.println(s);
FileManagerUtil.writeContentToFile("D:\macd\Desktop\", "test2.txt", s.getBytes("GB2312"));

测试后复现了此问题,那基本确定就是编码问题。经过一番思考和搜索,找到了一个汉字字符集编码查询的网站(https://www.qqxiuzi.cn/bianma/zifuji.php),可以根据字符的二进制值和指定的编码方式查出对应的字符。于是使用A8 52​和GB2312进行查询,发现果然没有匹配的字符,然后怀着试试看的心态换了另一个熟悉的编码GBK,结果竟然查到了正确的字符。

下面就简单了,将XML以GBK的编码方式读取,再使用GBK的方式写入,问题解决。

继续思考

问题是解决了,但还有几个小问题:

  1. 为什么编辑器中使用的也是GB2312编码方式打开的却可以正常显示字符呢?这个问题只能说编辑器做了很多优化
  2. GB2312和GBK之间有什么关系,上述解决方案是否会有风险,导致其他字符无法解析?这个问题特地查了GB2312和GBK的说明,GBK是GB2312的扩充,也就是说GBK是向后兼容GB2312的。另外查了一下这两种编码规范的编码表,针对上述问题中特殊符号类字符,GB2312是在A1-A9(即01-09区)表示,乍看一下是涵盖了上面的两个字符的,但是实际上GB2312只用了A1-A9的A0-FF区域。也就是说对于字符≧​(A8 52)所在的A8分区,GB2312只涵盖了A8A0-A8FF区域。而GBK则是在GB2312的基础上将空余的区域填充上了新的字符,具体可以见http://ff.163.com/newflyff/gbk-list/
  3. 另外,在搜索过程中还发现了新的汉字编码国家标准:GB18030,收录了更多的字符,而且还兼容GBK和GB2312