在之前的知识分享中(Python编码避坑指南——编码基础知识),我们为大家介绍了ASCII、Unicode、UTF-8等编码的相关概念。大家理解了这些编码概念之后,我们今天就来具体说说不同编码之间该如何转换;当我们处理的文本文件中包含中文的时候,我们如何才能正确读取文本文件中的内容。
1. 中文常见编码:GB2312、GBK
计算机最早是由美国发明的,他们制定了ASCII 码(前面文章有提到过,详情查看:Python编码避坑指南——编码基础知识)。后来计算机发展越来越广泛,世界各国为了可以在计算机保存他们的文字,用127号之后的空位来各种字符,从128到255这一页的字符集被称”扩展字符集”。但是原有的编号方法,已经再也放不下更多的编码。
GB2312:
等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。于是国人就自主研发,把那些127号之后的奇异符号们直接取消掉。规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1
用到0xF7
,后面一个字节(低字节)从0xA1
到0xFE
,这样我们就可以组合出大约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 -> GB2312utf8_gb2312 = gb2312_utf8.decode('utf-8').encode('gb2312')
-
UTF-8 转换为 GBK
utf8_gbk=gb2312_utf8.decode('utf-8').encode('gbk')
-
GBK 转换为 UTF-8
总结:如果是unicode与其他编码之间的转换,直接采用gbk_utf8 = utf8_gbk.decode('gbk').encode('utf-8')
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)