经过长时间的调查研究,初步确定部分文本在进行 GBK–>UTF-8编码转换时会遇到个别字节缺失导致的转码出错中断或转码结果乱码的情况。

案例1:

以 20230101/aliyun.20230101.8.武侠小说/1184.txt 第563行为例

如果我们用Python对它进行正常的编码转换通常都会遇到如下错误: UnicodeDecodeError: 'gbk' codec can't decode byte 0xac in position 46: illegal multibyte sequence, 这正是由于文本中单个字节丢失导致的。

转换出错原因(GBK编码的文本中丢失单个字节导致):

在GBK编码的定义中,00-7F是单字节,和ASCII保持一致,此范围内有96个文字和32个控制符号。从81-FE都是双字节编码,换句话说,几乎所有的中文汉字在GBK编码中都以双字节形式呈现。

例如汉字 `中国人` 的GBK字节编码为 \xd6\xd0\xb9\xfa\xc8\xcb, 其中 \xd6\xd0 是 “中”, \xb9\xfa 是 “国”, xc8\xcb 是 “人”,我们可以用Python对 \xd6\xd0\xb9\xfa\xc8\xcb 进行decode得到正确的汉字。

b'\xd6\xd0\xb9\xfa\xc8\xcb'.decode("gbk")
>> 中国人

假如我们把删掉 \xd6\xd0\xb9\xfa\xc8\xcb 的第2个字节 xd0,让它变成 \xd6\xb9\xfa\xc8\xcb ,再对它进行decode,程序就会报错了: UnicodeDecodeError: 'gbk' codec can't decode byte 0xfa in position 2: illegal multibyte sequence,程序在进行中文编码转换时按两个字节一组进行decode,但它无法正确的识别并转换 \xd6\xb9, 所以就报错了。

乱码原因的两种情况:

  1. 程序主动跳过出错的单字节,把单字节前后的内容拼起来作为一个新的双字节GBK字符,如果恰好新组成的双字节可以被转换成一个汉字(但已失去原意),程序会持续将错就错转换下去,直到下一个双字节无法识别抛出错误为止
  2. 文本丢失的字节不只是一个GBK编码单字节,而是可能分别丢失了一段文字中两个汉字的其中一个单字节,这样算下来,原本是3个汉字6个字节,结果变成了4个字节。程序还是可能将错就错把这四个字节转换成为了完全失去原意的汉字,或是无法识别而直接抛出错误。

举例说明:

我们把 \xd6\xd0\xb9\xfa\xc8\xcb 的1个字节和第3个字节删除掉,组成一个新的字节序列: \xd0\xfa\xc8\xcb,再对其进行GBK识别,原本的 中国人 被错误的识别成了 喧人,文本原意已经被混淆。

如果我们对1184.txt 这个文本的 563行内容进行忽略错误的编码转换会得到以下结果:

我们可以很清楚的看到这段文本因为 errors=“ignore” 的作用,会跳过识别错误的单字节,把后续的字节尽量按GBK编码的方式识别,但是早已经丢失了原意,这也是因为该段落文本中出现单个字节丢失的原因导致的。

为什么很多GBK文本出现一次或者多次单个字节丢失的情况我们还不得而知,不过基于以上的研究,我们可以设计一个新的编码识别转换的方法,就是想办法正确的标记单个字节丢失的真正位置,再交由人工进行识别处理,并借助其他自动化或者AI工具 ,补全这些字符。