一、缘起
前面的推文《用Python判断文本文件的编码方式》给出了一个判定文本编码方式的自定义函数,近来发现该函数对于某些特定的文本文件它会判断错误(参见《Python读取文本文件时ansi编码的妙用》一文)。
可见,判断编码的函数在功能上是有问题的,为此,需要从功能上修改这个函数。另外,函数可以代码行数上大幅缩减。二合一驱动,于是就有了今天的函数更新操作。
二、环境
Win 7中文专业版 32位 + Python 3.64
三、改进后的函数
废话少说,直接上改进后的函数代码。

四、测试
以推文《Python读取文本文件时ansi编码的妙用》中的“test.txt”为例,进行测试。
发现文本文件编码识别的结果居然是"utf-16-be",按这个编码读取文件,读出来是韩文。运行结果如下:
utf-16-be
뷓ퟅﶨ횴램뎤샏폖쪤쇋ꆰ쎷뮨떶ꆱ뗄헆쏅죋ꎬ솬쪤솽헳ഊ
这真是一个意外。赶紧将代码中的
encodings = ["utf-8-sig", "utf-8", "gbk", "utf-16", "utf-16-be", "ansi"]
变成
encodings = ["utf-8-sig", "utf-8", "gbk", "ansi", "utf-16", "utf-16-be"]
再次运行代码,结果成功识别为ansi编码,并成功读取其内容。输出
结果如下:
ansi
接着执法长老又胜了“梅花刀”的掌门人,连胜两阵
五、分析
双字节字符集gbk兼容中日韩的常用汉字,而unicode编码的文本也是双字节的,所以识别函数把ansi编码的中文文本错误识别成unicode编码的韩文也是情有可原的。就像ansi编码的“联通”总是被windows的记事本错误地识别成UTF8编码一样,有个别的文本判断出错在所难免。
因为咱们主要判断的文本是中国人写的文本,以ansi居多,所以再次改进的函数将ansi编码置于unicode的两个编码之前,让ansi编码得到优先判断。
六、改进
后来发现,测试上面的test.txt的时候,用编码gb18030也一样能愉快滴读取。于是,想到可以把gb18030作为中文文本编码的保底编码且中文系列编码优先判断,如果不是,再判定是否两个utf8编码之一,如果不是,再判定是否两个unicode编码之一,都不是的话,就判定为ansi编码。为此,再修改一下函数,干脆把gb2312也加入到中文系列编码当中。新的代码如下:

七、讨论
目前尚不知道有没有utf8编码或者unicode编码被错误判定为gb2312、gbk和gb18030编码的例子,因此这种按编码放置顺序来优先判断的做法实际上是一种权宜之计。不过,权宜之计总归是暂时的解决方案,出了问题还是得修改函数。一劳永逸的解决方案到底存在不存在呢?
初步想法是这样的:从这八种编码里面找出所有可能解码该文本文件的编码集合。如果集合只有一个元素,那么,一切OK,这唯一的编码肯定就是文本文件的编码。如果集合有一个以上的元素,就意味着文本文件至少有两种候选编码。我们可以用计算文本概率的方法来决定最佳编码方式。不过,这样一来,又得统计很多语料来计算文本概率。此乃后话,以后万一遇到不能判断的文本编码再来考虑启动这个思路。




