XML 编码问题排查

起因

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

排查过程

使用项目组提供的 demo 程序很快就复现了此问题。问题的特殊性在于文件绝大部分是正确的,只有特定的某几个字符乱码。查看二进制发现原先字符对于的二进制分别为 (A8 52)和 (A9 4F),乱码对应的二进制分别为 3F 523F 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