Python中同一字符串使用chardet检测字符编码,为何结果会不同?

手头处理一个文本,整体上来说是 utf-8 的,但每一行中不同的段可能是其他的中文编码,也就是中文的这几个编码 gb2312,gbk,big5 等。

发现循环进行 chardet 时候,单汉字容易被判断为 TIS-620,比如“翠”“纠”;但诡异的是,如果是单独进行检测,这样的单汉字又会被正确的判定为 utf-8。不太明白这其中的道理。单汉字常被解码成 ibm866,ibm855,iso-8859-2,koi8-r 等奇葩编码。

这样复杂的文本,如何才能正确解码呢?


Python中同一字符串使用chardet检测字符编码,为何结果会不同?

10 回复

字符检测不是 100%确定的。
是根据文件的字节在各种编码情况下的概率。

简单说假设 utf8 某个字节只可能是 00 - CC,但是这个字节是 DD,那么基本可以确定这个文件不是 utf8。
另外一个角度,这个文件的字节有 90%都符合一个正常 utf8 文件的字节排列,那么很大概率这个文件就是 utf8。

确定文件编码除了含 bom 的 utf8,包含有明确特征的文件头,其他格式基本就是靠猜。


这个问题其实挺常见的。chardet检测编码的原理是基于统计和概率模型,对文本内容进行采样分析。当同一字符串多次检测结果不同时,通常有以下几个原因:

  1. 采样随机性:chardet默认会从文本中采样一部分数据进行分析。如果采样点不同,统计特征可能有细微差异,导致置信度接近的编码之间结果波动。

  2. 文本长度过短:短文本(比如几个字)包含的统计特征不足,不同编码的统计模型可能得出相似的概率,导致每次检测的“猜测”结果在几个候选编码间摇摆。

  3. 编码本身模糊性:某些字节序列在多种编码中都是合法的(比如ASCII范围内的字符在UTF-8、GBK、ISO-8859-1中表现一致),chardet可能会给出多个可能性相近的结果。

如果你需要稳定结果,可以尝试:

  • 增加文本长度
  • 使用detect()函数的should_rename_legacy参数
  • 或者对同一文本多次检测取置信度最高的结果

不过对于短文本或模糊文本,编码检测本身就不完全可靠,最好能通过元数据或协议确定编码。

总结:这是统计检测方法的固有特点,短文本或模糊文本结果易波动。

这样就会在不同的检测时点,检测结果不一样?为什么总觉得猜应该猜的一致才对呢?
另外,既然是猜,我也知道这个文件里面没有 ascii,utf-8,gbk,gb2312,gb10830,big5 之外的编码,是不是可以武断一点,如果发现 chardet 检测出上述编码之外的,我就用 utf-8 去碰运气?

#2 chardet 是根据概率分布来识别的,你用单个字符做输入完全没有意义,每次结果不一样说明 chardet 用了随机算法。
如果自己识别,可以先用 UTF-8 解码,因为非 UTF-8 编码的数据按 UTF-8 解码,大概率会解码失败。对解码失败的段落再另行处理吧

那多少个字符会比较准?
中文字符的话,应该 gbk,gb2312,gb10830,big5 就这几个了吧。会不会出现两种或者多种都能解码的,但解码只有一个正确的?

当然 utf8 是 ASCII 的超集,gbk 是 gb2132 的超集。
解码都能解码,最明显的就是 gbk 当 utf8 来解码就会乱码。但是电脑不知道是否乱码,只有人能看出来

这个乱码判断真的就只能是判断图像了?我这个文本太奇葩了,一行里面掺杂太多不同的编码。chardet 有没有可能不适用随机算法?让每次的输出都相同?大规模判断就有问题,单个测试就能正确解码,这个现象很奇葩。

虽然你在 V2EX 问过好多遍了,但是这次的需求说的更为清楚,所以我可以重新回答一下。

这次你说清楚了你的解码内容只有汉字。那么,不妨直接调用 chardet 提供的 big5prober、gb2312prober、utf8prober,然后选择其中置信度最高的一个,这样自然不可能判定为除此三种之外的其他编码。

如果这样的效果仍然不能被接受(也就是说,被误判为三种当中的另外两种编码),你可以考虑根据实际情况调整三种编码的权重。或者拟定一个常用字范围,如果看起来「像乱码」(生僻字),再试着有另一种解码器。

你要是知道编码检测的原理就没这个疑问了🤓

感谢。非常好的思路。看起来这种判断还是挺复杂的,最终还是免不了肉眼“看”

回到顶部