Python编码避坑指南-编码转换实战

  在之前的知识分享中(Python编码避坑指南——编码基础知识),我们为大家介绍了ASCII、Unicode、UTF-8等编码的相关概念。大家理解了这些编码概念之后,我们今天就来具体说说不同编码之间该如何转换;当我们处理的文本文件中包含中文的时候,我们如何才能正确读取文本文件中的内容。

1. 中文常见编码:GB2312、GBK

  计算机最早是由美国发明的,他们制定了ASCII 码(前面文章有提到过,详情查看:Python编码避坑指南——编码基础知识)。后来计算机发展越来越广泛,世界各国为了可以在计算机保存他们的文字,用127号之后的空位来各种字符,从128到255这一页的字符集被称”扩展字符集”。但是原有的编号方法,已经再也放不下更多的编码。

  • GB2312
    等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。于是国人就自主研发,把那些127号之后的奇异符号们直接取消掉。规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到 0xF7,后面一个字节(低字节)从0xA10xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312″。GB2312 是对 ASCII 的中文扩展。

  • GBK
    但是中国的汉字太多了,后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是 扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK 包括了 GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK 扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。

2. 通过Unicode编码进行不同编码之间的互相转化

  不同的编码之间不能互相识别,不能相互转化,会报错或出现乱码。Python3默认的编码是Unicode,如果我们希望实现不同编码之间的互相转换, 都要先decode解码为unicode编码, 然后通过unicode再encode编码为想要的编码。

  • Unicode 转换为 GB2312
    a = 'Python与临床科研' #由于Python3 的字符串类型默认为Unicode,所以不需要decode()
    unicode_gb2312 = a.encode('gb2312')
    print(a, '[gb2312]', unicode_gb2312)
    
    输出结果:
    Python与临床科研 [gb2312] b'Python\xd3\xeb\xc1\xd9\xb4\xb2\xbf\xc6\xd1\xd0'
    
  • GB2312 转换为 Unicode
    gb2312_unicode = unicode_gb2312.decode('gb2312')
    
  • GB2312 转换为 UTF-8
    转换步骤:GB2312 -> Unicode -> UTF-8
    gb2312_utf8=unicode_gb2312.decode('gb2312').encode('utf-8')
    print(a,'[utf-8]',gb2312_utf8)
    
    输出结果:
    Python与临床科研 [utf-8] b'Python\xe4\xb8\x8e\xe4\xb8\xb4\xe5\xba\x8a\xe7\xa7\x91\xe7\xa0\x94'
    
  • UTF-8 转换为 GB2312
    转换步骤:UTF-8 -> Unicode -> GB2312
    utf8_gb2312 = gb2312_utf8.decode('utf-8').encode('gb2312')
    
  • UTF-8 转换为 GBK
    utf8_gbk=gb2312_utf8.decode('utf-8').encode('gbk')
    
  • GBK 转换为 UTF-8
    gbk_utf8 = utf8_gbk.decode('gbk').encode('utf-8')
    
    总结:如果是unicode与其他编码之间的转换,直接采用decode('编码类型')将其他编码转换为unicode,采用encode('编码类型')将unicode的编码转换为其他编码类型。其他编码之间转换都要先decode解码为unicode编码, 然后通过unicode再encode编码为想要的编码。

3. 简单解决Python文件中文编码问题

  • 已知文件编码类型
    添加encode参数,使为对应的文件编码类型。

    import pandas as pd
    pd.read_csv("example.csv",encoding='gb2312')
    
  • 文件编码类型未知 / 批量处理时多个文件编码类型不同
    对于未知编码的文本文件,要把它转换成字符串,需要先“猜测”编码。猜测的方式是先收集各种编码的特征字符,根据特征字符判断,就能有很大概率“猜对”。当然,我们肯定不能从头自己写这个检测编码的功能,这样做费时费力。chardet这个第三方库正好就派上了用场。用它来检测编码,简单易用。

    安装chardet
    如果安装了Anaconda,chardet就已经可用了。否则,需要在命令行下通过pip安装:

    conda install -c anaconda chardet
    

    使用chardet

    import chardet
    import pandas as pd
    
    # 获取文件编码类型
    def get_encoding(file):
        # 二进制方式读取,获取字节数据,检测类型
        with open(file, 'rb') as f:
            return chardet.detect(f.read())['encoding']
    
    filename = "example.csv"
    # 根据文件编码读取文件内容
    pd.read_csv(filename, encoding=get_encoding(filename))
    

4.【更新】批量转换文件编码

看到有朋友在评论区提问批量转换文件编码,特更新以下代码,利用chardet和循环遍历目录,实现批量编码转换。
代码参考自博主「罗小通」

# -*- coding: utf-8 -*-
import os, chardet, codecs, re
#文件类型扩展名  文件列表
FileType, FileList = [], []

def get_file_list(Dir):
# 获取指定目录下所有指定类型文件

    if len(Dir.strip(' ')) == 0:
        return 
    dirList = [os.path.join(Dir, f) for f in os.listdir(Dir)]
    fileList = [f for f in dirList if os.path.isfile(f) and os.path.splitext(f)[1] in FileType]
    folderList = [f for f in dirList if os.path.isdir(f)]
    FileList.extend(fileList)
    # 递归字文件夹
    for subfolder in folderList:
        get_file_list(subfolder)
    

def convert_2_target_coding(coding='utf-8'):
# 转换成目标编码格式

    for filepath in FileList:
        with open(filepath, 'rb') as f:
            data = f.read()
            codeType = chardet.detect(data)['encoding']

        if codeType not in (coding, 'ascii'):
            with codecs.open(filepath, 'r', codeType) as f:
                content = f.read()
            with codecs.open(filepath, 'w', coding) as f:
                f.write(content)
            print(filepath + '\n')

            
if __name__ == '__main__':
    # 获取目录
    WorkDir = str(input('input target folder\n\t:'))
    # 目标编码格式
    TargetCoding = str(input('target coding(default to utf-8)\n\t:')).lower()
    # 文件类型扩展名
    FileType = re.split(r'\s+', str(input('file type(filename extension, such as .c .h)\n\t:')))
    os.chdir(WorkDir)
    get_file_list(WorkDir)
    convert_2_target_coding(TargetCoding)

你可能感兴趣的:(Python编码避坑指南-编码转换实战)