拿同一批数据喂给 chardet 7.7.0 和 charset-normalizer 3.5.2,两者给出的编码判断完全一致,但置信度的含义完全不同。

chardet 的置信度随样本变长而上升:同样是 GBK 文本,5 个字时 0.10,500 个字升到 0.67。charset-normalizer 无论给多少字,永远是 1.00。

前者的数字能用来判断「该不该信」,后者的数字不能。差别就在这里。

两边的判定结果一致吗

一致。五种编码、四种长度,两个库给出的编码名全部相同:

真实编码字数chardet 判定置信度charset-normalizer 判定数值
GBK5gb180300.10gb180301.00
GBK20gb180300.34gb180301.00
GBK100gb180300.64gb180301.00
GBK500gb180300.67gb180301.00
Big55big50.13big51.00
UTF-8任意utf-80.99utf-81.00
UTF-16任意utf-161.00utf-161.00

GBK 被判成 gb18030 不算错:GB18030 是 GBK 的超集,用它解码 GBK 的字节不会出问题。反过来说,如果你必须知道原始文件到底是 GBK 还是 GB18030 写的,这套检测给不出答案,只能自己按业务约定判断。

为什么要看置信度

因为编码判断本身是概率问题。同一串字节可能有好几种合法解读,库只能挑一个最像的。

chardet 把「有多像」用置信度表达出来。GBK 那四行的差别最清楚。

  • 5 个字 → 0.10:置信度只有 0.10
  • 20 个字 → 0.34:置信度 0.34
  • 100 个字 → 0.64:可用
  • 500 个字 → 0.67:稳定在这个区间,不再往上走

500 字也只有 0.67,说明 GBK 和 GB18030 在字节层面本来就难以区分,不是样本不够的问题。

反过来看 UTF-8 和 UTF-16:不管几个字,0.99 和 1.00。这两类编码的字节特征固定,几乎没有第二种解读。

1.00 这个数字可信吗

不能当可靠度用。

charset-normalizer 返回的 1.00 是「1 减去 chaos 值」算出来的。它这套指标衡量的是解码后文本的混乱程度,不是「有多少把握是这个编码」。

证据就是那行 Big5:5 个字符的样本,chardet 只给 0.13(置信度低),charset-normalizer 给 1.00。同一个样本,一个给 0.13,一个给 1.00。后者这个 1.00 不能用来判断可靠性。

要用 charset-normalizer 判断可靠性,应该看它返回的其他字段(比如 `could_be_from_charset` 里的候选列表),而不是那个 chaos 反算值。

短文本该怎么处理

基于上面的数据,短文本(100 字以下)的编码判断不可靠,尤其 GBK 系。可行的做法是:

  • 先按最可能的编码解一次,检查有没有替换字符(U+FFFD)或异常控制字符
  • 解出来的文本能正常显示、标点没有乱码,就接受
  • 解不了再换下一个候选,别只看返回值
  • GBK 系优先试 GB18030,它是超集,能覆盖 GBK 的全部字节

100 字以上、且 chardet 置信度过 0.6 的,可以直接采用。

混进乱码会怎样

我用 GBK 数据后面接了两个字节 `\xff\xfe`,结果 chardet 返回 KOI8-R(俄语编码),置信度 0.00。

文本主体明明是中文,它给的是不着边的答案。这种数据在断点续传、文件拼接出错时很常见,说明置信度 0.00 是有意义的信号:当它给 0 的时候,结果基本不用看。

装的时候要注意源

清华镜像装不了这个包,报「找不到任何版本」。查下来不是包没同步:镜像返回的索引格式是 `repository-version 1.0`,官方源是 `1.4`,老格式缺 `requires-python` 字段,pip 26 就把所有版本判成不兼容。

换官方源即可:

pip install -i https://pypi.org/simple chardet

4 秒装完,版本 7.7.0。同样的问题今天在 cn2an、glances、jieba 上也遇到了,规律是同一类。