前言:这是之前处理一批老旧文件遇见的坑,在这批文件里面,有个记录更新历史的text文件,需要在场景文件做更新的同时记录下更新的内容,一顿操作之后发现在其中某些txt文件中新录入的中文是乱码,一番查证之下发现,这批文件里的编码并不相同,有些是默认的gbk,有些的utf-8…还有一些是全英文的ASCII…在python里面要处理字符串编码确实挺让人头疼的,以下细细说道。
ps:适用python3.x
一般读取文件方式
在python中对txt文件操作,需要先打开文件,再进行读写,这里使用使用Python内置的open()函数(具体该函数的使用在此不表,可参考官方文档):
with open('/Users/admin/test.txt', 'a') as f:
f.write(u'你好!')
字符编码
由于系统默认创建的txt文件编码为“gbk”,而要读取非UTF-8编码的文本文件,需要给open()函数传入encoding参数)(ps:python2.x中没有此参数,可以不写):
with open('/Users/admin/test.txt', 'a', encoding='gbk') as f:
f.write(u'你好!')
以上,是对已知编码的文件进行编辑的方式。
检测编码
那要对一批未知编码方式的文件进行操作,就需要先查询该文件的编码,python就有这么一个第三方库chardet,用它来检测编码(python默认没有这个库,需要自行安装),至此,这段代码有了第三个方案:
import chardet
def get_encoding(file):
with open(file,'rb') as f:
return chardet.detect(f.readline())['encoding']
#{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}
txt_code = get_encoding('/Users/admin/test.txt')
with open('/Users/admin/test.txt', 'a', encoding=txt_code) as f:
f.write(u'你好!')
在上面这段代码中,open(file,‘rb’)中的‘rb’是只用二进制模式去读取文件,这样不会造成因为对文件编码错误造成读取错误的,f.readline()仅去读取文件中第一行内容,便于节约资源
混合中英文字符
当txt文件中仅有英文字符时,使用上面那种方案,会因为chardet检测到的编码格式为ASCII而报以下错误:
UnicodeEncodeError: 'ascii' codec can't encode character '\xf1' in position 11: ordinal not in range(128)
再仔细排查,发现,无论是以“gbk”还是“urf-8”编码的纯英文文件,检测编码都是‘ascii’:
# gbk编码:英文是ascii,中文是GB2312(GBK的上一版中文字符集),language字段指出的语言是'Chinese'
print("str1", check_char("hello world".encode("gbk"))) # str1 {'encoding': 'ascii', 'confidence': 1.0, 'language': ''}
print("str2", check_char("你好".encode("gbk"))) # str2 {'encoding': 'GB2312', 'confidence': 0.99, 'language': 'Chinese'}
# utf-8编码: 英文还是ascii,中文是utf-8了,但是language没有指出,是因为utf-8适用的太多了
print("str3", check_char("hello world".encode("utf-8"))) # str3 {'encoding': 'ascii', 'confidence': 1.0, 'language': ''}
print("str4", check_char("你好".encode("utf-8"))) # str4 {'encoding': 'utf-8', 'confidence': 0.9690625, 'language': ''}
这就很头疼了,那要在纯英文字符后面添加中文字符究竟要使用哪一种编码呢???答案是,随便哪一种都可。。。。。于是,代码再次修改:
import chardet
def get_encoding(file):
with open(file,'rb') as f:
txt_code = chardet.detect(f.read())['encoding']
#{'encoding': 'utf-8', 'confidence': 0.99, 'language': ''}
if txt_code == 'ascii':
return 'utf-8'
return txt_code
txt_code = get_encoding('/Users/admin/test.txt')
with open('/Users/admin/test.txt', 'a', encoding=txt_code) as f:
f.write(u'你好!')
在这段代码里,把f.readline()又改回了f.read(),因为很有可能在文件的第一行就遇见全英文,而中文字符躲在后面,指定的utf-8编码很可能不适合该文件而导致解码出错。
好,至此,纠结了好几个月的问题,终于解决了。。。撒花~~~