0. 写在前面
起因:之前写个数据预处理程序的时候遇到了点问题,用re模块的正则查找方法search时总是找不出来(找错了或者出乱码),于是捣鼓捣鼓。
经过:查资料,做实验,猜想:发现用utf8编码的str类型的字符串在search方法中行不通,因为str是字节串,和字符之间没有固定的一一对应的关系,正则没法用字节串来进行正确匹配。
结果:把正则式和目标字符串都使用unicode类型,unicode和字符之间是两个字节对应一个字符的关系,正则可以根据这个来对字符进行匹配。
后续:突然觉得应该总结一下编码问题,防止再次入坑。于是有了此文。
1. ascii, unicode, utf8
ascii码:最早的编码,只有127个字符,包含英文字母,数字,标点符号和一些其它符号。一个字节表示一个字符。
unicode(统一码):一个字节不够放,全世界有各种语言的字符需要编码,于是unicode给所有的字符都设定了唯一编码。通常都是用两个字节表示一个字符(有些生僻的字要用四个字节)。所以,要理解一点:下文中提到到的unicode编码是双字节编码(一个字符两个字节)。
uft8:对于ascii编码的那些字符,只需要1个字节,unicode给这些字符也设定2个字节,如果一篇文章全是英文(ascii字符),就浪费了很多空间(本来1个字节可以存储的,用了2个字节),所以产生了utf8。utf8是一种变长的编码方式,根据不同的符号变化字节长度,把ascii编码成1个字节,汉字通常编码成3个字节,一些生僻的字符编码成4~6个字节。
在计算机内存中,统一使用Unicode编码。
在python中,建议程序过程中统一使用unicode编码,保存文件和读取文件时使用utf8(在读写磁盘文件时候用utf8进行相应的decode和encode,关于decode和encode见下文第4点)。
2. encoding声明
python默认使用ascii编码去解释源文件。
如果源文件中出现了非ASCII码字符,不在开头声明encoding会报错。
可以声明为utf8,告诉解释器用utf8去读取文件代码,这个时候源文件有中文也不会报错。
# encoding=utf8 如果不加这一行会报错 print '解释器用相应的encoding去解释python代码'
3. python2.7中的str和unicode
debugger的时候会发现,python2.7中的字符串一般有两种类型,unicode和str。
str为字节码,会根据某种编码把字符串转成一个个字节,这个时候字符和字节没有所谓固定的一一对应的关系。
unicode则是用unicode编码的字符串,这个时候一个字符是对应两个字节的,一一对应。
直接赋值字符串,类型为str,str为字节串,会按照开头的encoding来编码成一个个的字节。
赋值的时候在字符串前面加个u,类型则为unicode,直接按照unicode来编码。
s1 = '字节串' print type(s1) #输出 <type 'str'>,按照开头的encoding来编码成相应的字节。 print len(s1) #输出9,因为按utf8编码,一个汉字占3个字节,3个字就占9个字节。 s2 = u'统一码' print type(s2) #输出 <type 'unicode'>,用unicode编码,2个字节1个字符。 print len(s2) #输出3,unicode用字符个数来算长度,从这个角度上看,unicode才是真正意义上的字符串类型
4. python2.7中的encode和decode
encode的正常使用:对unicode类型进行encode,得到字节串str类型。也即是unicode -> encode(根据指定编码) -> str。
decode的正常使用:对str类型进行decode,得到unicode类型。也即是str -> decode(根据指定编码) -> unicode。
注意:encode和decode的时候都是需要指定编码的。
因为在编码的时候要知道原来的编码是什么和按照什么新编码方式进行编码,要用到两种编码,这里默认有一个unicode,所以需要再指定一个编码方式。解码的时候也是一个道理。
这两个方法就是在unicode和str之间用指定编码进行转换。
s3 = u'统一码'.encode('utf8') print type(s3) # 输出 <type 'str'> s4 = '字节串'.decode('utf8') print type(s4) #输出 <type 'unicode'>
encode的不正常使用(不建议):对str类型进行encode,还是会得到str类型的。这个时候python会用默认的系统编码decode成unicode类型,再用你给出编码进行encode。(注意这里的系统编码不是开头的encoding!这个见下文第5点)
decode的不正常使用:对unicode类型进行decode,直接报错。
5. 修改系统默认编码
系统默认使用ascii编码,需要进行相应的修改。
这个编码和开头的encoding不同之处在于,开头的encoding是对于文件内容的编码,这里的编码是一些python方法中默认使用的编码,比如对str进行encode的时候默认先decode的编码。
import sys reload(sys) sys.setdefaultencoding('utf8') s = '字节串str' s.encode('utf8') #等价于 s.decode(系统编码).encode('utf8')
6. 查看文件编码
import chardet with open(filename,'r') as f: data = f.read() return chardet.detect(data)
7. 一般的处理要点
(1) 首先把源文件的默认encoding和系统默认编码改为utf8
(2) 程序执行过程统一使用unicode类型(看情况,也不用那么死,平常用str也没事。只是一个建议:遇到编码困扰可以思考是不是没统一用unicode的问题)
(3) 对于读写文件,得到的是str,对str进行相应的encode和decode就可以了。
总结一下就是:
设置相应的默认编码为utf8;
读文件拿到str类型:str -> decode('utf8') -> unicode
程序处理:用unicode
写文件:unicode -> encode('utf8') -> str,用str类型写入文件
当然前提是文件都是utf8格式的啦,包括源文件和读写的数据文件。