记一件使用Python的Chardet库进行txt读取GB2312码时候遇到的小事

背景

我的计算机里存有许许多多的小黄文,都是以前上中学时候从文曲星时代就攒下来的,大多是txt格式。

现在有了手机,装了一个叫做Good Reader的App,每次遇到不同的编码的txt,都要重新手动选择切换编码格式,非常繁琐,影响兴致,所以萌生了利用Python来批量转换txt编码格式的念头。

有人说,你这个人不正经,居然看小黄文。

呔,正经人谁看CSDN呐。


环境

Python 3.8.9 64 bit + Windows 7 Pro 64 bit 英文版
chardet 4.0.0


在摸索中前进的代码

from chardet.universaldetector import UniversalDetector

def detcect_encoding(filepath):
    """检测文件编码
    Args:
        detector: UniversalDetector 对象
        filepath: 文件路径
    Return:
        file_encoding: 文件编码
        confidence: 检测结果的置信度,百分比
    """
    detector = UniversalDetector()
    detector.reset()
    for each in open(filepath, 'rb'):
        detector.feed(each)
        if detector.done:
            break
    detector.close()
    file_encoding = detector.result['encoding']
    confidence = detector.result['confidence']
    if file_encoding is None:
        file_encoding = 'unknown'
        confidence = 0.99
    return file_encoding, confidence * 100

filename = '一對青年夫婦的淫穢生活.txt'

file_encoding, confidence = detcect_encoding(filename)

with open(filename, 'r', encoding=file_encoding, errors='ignore') as f:
    text = f.read()

print(f'[+] {filename}: 编码 -> {file_encoding} (置信度 {confidence}%) [+]')

执行一下代码,看看源文件编码啥格式:

[+] 一對青年夫婦的淫穢生活.txt: 编码 -> GB2312 (置信度 99.0%) [+]


很好,GB2312,国标,那还不简单,开车!

如下代码写起,走你:

target_encoding = 'utf-8'

if file_encoding != 'unknown' and confidence > 0.75:
    with open(filepath, 'r', encoding=file_encoding, errors='ignore') as f:
        text = f.read()

    outpath = os.path.join(outputpath, filename)
    with open(outpath, 'w', encoding=target_encoding, errors='ignore') as f:
        f.write(text)
    
    print(
        f'[+] 转码成功: {filename}({file_encoding}) -> {outpath}({target_encoding}) [+]')

怀着激动的心情,导入手机,打开,这怎么是火星文?

再回溯一下,在pc上看看txt,发现转换出来的文章总是夹杂大量的乱码,非常不可思议,挑了一段应该可以过审的片段给大家看:

  到了村^叔叔家,已得到消息的叔叔鹪е一群堂弟堂妹候在院T口,
一到我,立刻n上L短,搞得我接不暇。

  珍妮W著我的樱D出一句生硬的hZU「漭铮ㄊ迨搴茫,申申蒿
(鸷茫。」逗得人哄然大笑。

  我把淼男《Y物分送了人。Y物m小,r值也不很高,但都是正宗的
美,小巧而精致。H戚g天喜地,x不停。

原文可是这样的啊:

  到了村頭叔叔家,已得到消息的叔叔嬸嬸早帶著一群堂弟堂妹候在院門口,
一見到我們,立刻圍攏上來問長問短,搞得我們應接不暇。

  珍妮學著我們的樣,擠出一句生硬的漢語︰「樹樹蒿(叔叔好),申申蒿
(嬸嬸好)。」逗得人們哄然大笑。

  我們把帶來的小禮物分送了眾人。禮物雖小,價值也不很高,但都是正宗的
美國貨,小巧而精致。親戚們歡天喜地,謝個不停。

排故

用VS Code打开源txt看了一下,确认没有错,编码的确是GB2312,那么问题出在哪里呢?

让我们来换一下解码的参数:

with open(filename, 'r', encoding='gb2312', errors='ignore') as f:
    text = f.read()
print(text)
print(f'[+] {filename}: 编码 -> {file_encoding} (置信度 {confidence}%) [+]')
珍妮W著我的樱D出一句生硬的hZU「漭铮ㄊ迨搴茫,申申蒿
(鸷茫。」逗得人哄然大笑。

  我把淼男《Y物分送了人。Y物m小,r值也不很高,但都是正宗的
美,小巧而精致。H戚g天喜地,x不停。
[+] gb.txt: 编码 -> GB2312 (置信度 99.0%) [+]

啧啧,车都开脸上了,你居然还骗我说这是GB2312编码。
忽然灵机一动,会不会是GB2312的命名问题。

所谓GB2312也好,GBK也好,GB18030也好,它们都是一脉相承下来,向前兼容的。
那我来试一下GBK来解码好了:

with open(filename, 'r', encoding='gbk', errors='ignore') as f:
    text = f.read()
print(text)
珍妮學著我們的樣,擠出一句生硬的漢語︰「樹樹蒿(叔叔好),申申蒿
(嬸嬸好)。」逗得人們哄然大笑。

  我們把帶來的小禮物分送了眾人。禮物雖小,價值也不很高,但都是正宗的
美國貨,小巧而精致。親戚們歡天喜地,謝個不停。

果然,问题就是出在这里。
因为某些原因,我以为的GB2312并不是系统以为的GB2312,在它那里,这就是GBK或者是GB18030,本着兼容性的目的,再改成GB18030试一试:

with open(filename, 'r', encoding='gb18030', errors='ignore') as f:
    text = f.read()
print(text)
珍妮學著我們的樣,擠出一句生硬的漢語︰「樹樹蒿(叔叔好),申申蒿
(嬸嬸好)。」逗得人們哄然大笑。

  我們把帶來的小禮物分送了眾人。禮物雖小,價值也不很高,但都是正宗的
美國貨,小巧而精致。親戚們歡天喜地,謝個不停。

OK,破案了,这一下再转换一次,重新上代码,愉快地开车啊:

if file_encoding != 'unknown' and confidence > 0.75:
    
    if file_encoding == 'GB2312':
        file_encoding = 'GB18030'

    with open(filepath, 'r', encoding=file_encoding, errors='ignore') as f:
        text = f.read()

    outpath = os.path.join(outputpath, filename)
    with open(outpath, 'w', encoding=target_encoding, errors='ignore') as f:
        f.write(text)

    print(
        f'[+] 转码成功: {filename}({file_encoding}) -> {outpath}({target_encoding}) [+]')
 [+]')

翻车之后的经验总结

怀着喜悦的心情,重新上传到手机里,耶,好,这下可以打开了,没有问题。

可是好景不长,没看几篇,到了下一篇文章,又出错了,虽然可以正确显示“每一个能够显示的文字”,但是中间莫名其妙少了一大段是怎么回事?

因为某种不清楚的原因,Good Reader在读取某些UTF-8的txt文件时会报错。即使是在PC上可以正常打开的文件,到了我的iPhone 6s上就会出现莫名其妙的错误。

也不知道这个锅是我的,是微软的,是苹果的,还是Good Reader的开发者的。
总之,UTF-8既然不好使,我们就换一个新的格式,UTF-16走起。

从地上爬起来,把车扶起来,继续修改参数,加一个遍历,批量转换txt,有了横空出世的新一版代码:

from chardet.universaldetector import UniversalDetector
import os


def detcect_encoding(filepath):
    """检测文件编码
    Args:
        detector: UniversalDetector 对象
        filepath: 文件路径
    Return:
        file_encoding: 文件编码
        confidence: 检测结果的置信度,百分比
    """
    detector = UniversalDetector()
    detector.reset()
    for each in open(filepath, 'rb'):
        detector.feed(each)
        if detector.done:
            break
    detector.close()
    file_encoding = detector.result['encoding']
    confidence = detector.result['confidence']
    if file_encoding is None:
        file_encoding = 'unknown'
        confidence = 0.99
    return file_encoding, confidence * 100


if __name__ == '__main__':
    target_encoding = 'utf-16'
    rootdir = os.path.join(r'D:\source')
    for (_, _, filenames) in os.walk(rootdir):
        for filename in filenames:
            filepath = rootdir + '\\' + filename
            file_encoding, confidence = detcect_encoding(filepath)

            if file_encoding != 'unknown' and confidence > 0.75:
               
                if file_encoding == 'GB2312':
                    file_encoding = 'GB18030'

                with open(filepath, 'r', encoding=file_encoding, errors='ignore') as f:
                    text = f.read()

                outpath = os.path.join(r'D:\result', filename)
                with open(outpath, 'w', encoding=target_encoding, errors='ignore') as f:
                    f.write(text)

                print(
                    f'[+] 转码成功: {filename}({file_encoding}) -> {outpath}({target_encoding}) [+]')

自动化什么的,最方便了

[+] 转码成功: 9.11.txt(GB18030) -> D:\result\9.11.txt(utf-16) [+]
[+] 转码成功: 9.25.txt(GB18030) -> D:\result\9.25.txt(utf-16) [+]
[+] 转码成功: 9.27.txt(GB18030) -> D:\result\9.27.txt(utf-16) [+]

建议

之所以没有覆盖源文件,是因为万一翻车了,你至少宝贝们还在对吧。
所以我建议各位还是把新产生的文件输出在不同的文件夹里,做好备份,安全第一啊。


全文总结

知道你们的时间很宝贵,很多人根本没有耐心看完一整篇文章。

总结一下全文:

VS Code中,用Python的with open()函数,如果加上encoding=gb2312,有一定概率会出现乱码。
即使打开的是真的GB2312的txt文件,也依然有乱码的可能性。
可能本来的文件就是GBK或者GB18030编码,但是被Chardet识别成了GB2312,也有可能是Python调用的编码器的命名不规范,GB2312和GBK混淆了。
各种可能性都存在。
当使用GB2312乱码的时候,换用GB18030试试,有时候就能解决乱码的问题。


后记

这两天我看到微博上有个人说:

之前有一位厨师评论说,像王刚、老饭骨这样做了十几年甚至几十年菜的人在视频中透露出的很多技法,如果在前互联网时代想学习,那是要在后厨帮忙两三年后师傅才肯教的,不然只能偷师。现在几百万人看个几分钟的视频就能学会,这就是互联网的伟大。

伟大的互联网让你能够看到这里,也能够让我从众多高手身上学到各种有用的技巧,得以写出这篇文章。

希望能帮到同样遇到类似问题的朋友,感激不尽。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值