0. 写在前面python
原由:以前写个数据预处理程序的时候遇到了点问题,用re模块的正则查找方法search时老是找不出来(找错了或者出乱码),因而捣鼓捣鼓。后端
通过:查资料,作实验,发现用utf8编码的str类型的字符串在search方法中行不通,由于str是字节串,和字符之间没有固定的一一对应的关系,正则无法用字节串来进行正确匹配。安全
结果:把正则式和目标字符串都使用unicode类型,unicode和字符之间是两个字节对应一个字符的关系,正则能够根据这个来对字符进行匹配。python2.7
后续:忽然以为应该总结一下编码问题,防止再次入坑。因而有了此文。机器学习
1. ascii, unicode, utf8函数
ascii码:最先的编码,只有127个字符,包含英文字母,数字,标点符号和一些其它符号。一个字节表示一个字符。学习
unicode(统一码):一个字节不够放,全世界有各类语言的字符须要编码,因而unicode给全部的字符都设定了惟一编码。一般都是用两个字节表示一个字符(有些生僻的字要用四个字节)。因此,要理解一点:下文中提到到的unicode编码是双字节编码(一个字符两个字节)。编码
uft8:对于ascii编码的那些字符,只须要1个字节,unicode给这些字符也设定2个字节,若是一篇文章全是英文(ascii字符),就浪费了不少空间(原本1个字节能够存储的,用了2个字节),因此产生了utf8。utf8是一种变长的编码方式,根据不一样的符号变化字节长度,把ascii编码成1个字节,汉字一般编码成3个字节,一些生僻的字符编码成4~6个字节。spa
在计算机内存中,统一使用Unicode编码。debug
在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才是真正意义上的字符串类型
来看点现实的例子,好比咱们要从一个文件中找出中全部后两位是'学习'的词语,在进行判断的时候:
s = '机器学习' s[-2:] == '学习‘ # 返回false,平时写程序可能会觉得相等。 # 这里的”学习是用开头的encoding声明解释的,我开头用的是utf8,汉字占3个字节,因此“学习”占了6个字节),而s[-2:]取的是最后两个”双字节“,因此不相同。 s = u'机器学习' s[-2:] == u'学习’ # 返回true,这也是为何说unicode是真正意义上的字符串类型。由于使用的是unicode,”学习“占的是两个”双字节“,一个"双字节“一个字。
对于常常处理中文字符串的人,统一用unicode就能够避免这个坑了。
虽然有些字符串处理函数用str也能够,应该是函数里面帮你处理了编码问题。
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,由于encode须要的是unicode类型,这个时候python会用默认的系统编码decode成unicode类型,再用你给出编码进行encode。(注意这里的系统编码不是开头的encoding,具体例子见下文第5点)
decode的不正常使用:对unicode类型进行decode,python会用默认的系统编码encode成str类型,再用你给出的编码进行decode。
因此改好对应的系统默认编码,就算不正常使用,也不会报错啦。不过多拐了一下路,我的不喜欢这样。
5. 修改系统默认编码
系统默认使用ascii编码,须要进行相应的修改。
这个编码和开头的encoding不一样之处在于,开头的encoding是对于文件内容的编码。
这里的编码是一些python方法中默认使用的编码,好比对str进行encode的时候默认先decode的编码,好比文件写操做write的encode的编码(关于文件读写见下文第7点)
import sys reload(sys) sys.setdefaultencoding('utf8') s = '字节串str' s.encode('utf8') #等价于 s.decode(系统编码).encode('utf8')
关于系统默认编码发挥做用的地方,来看看另外一个例子。
import sys print sys.getdefaultencoding() # 输出ascii s = 'u华南理工大学' print s[-2:] == '大学' # 返回False,并有warning提醒 reload(sys) sys.setdefaultencoding('utf8') print s[-2:] == '大学' # 返回True
根据结果得知:python在用==比较时,若是第一个操做符是unicode而第二个不是的话,会自动用系统默认编码帮第二个操做符decode。
PS:为何须要reload(sys)呢。首先,reload是用于从新加载以前import的模块。
这里须要从新加载sys的缘由是:python在加载模块时候删除了sys中的setdefaultencoding方法(多是出于安全起见),因此须要reload这个sys模块。
这里再举个简单例子,好比我要修改keras的后端,从tensorflow改为theano,修改后须要从新加载keras的backend模块才能修改为功。
import keras.backend as K k.backend() # 一开始是u'tensorflow' import os os.environ['KERAS_BACKEND'] = 'theano' K.backend() # 修改后仍是u'tensorflow' reload(K) k.backend() # reload以后后端才变成u'theano'
6. 查看文件编码
import chardet with open(filename,'r') as f: data = f.read() return chardet.detect(data)
7. 文件读写
首先要记住,读出和写入,这两个文件的关口都是用str类型的,就是一个个字节。
python中内置的默认open在读取文件的时候以字节串str的形式,读出一个个字节。读取后要用正确的编码才能decode成正确的unicode,因此要知道原来在文件中的编码。
写文件的时候也是一个道理,用str类型,以字节的形式写入,这个str是以某种编码方式编码的,要注意用正确的编码方式编码,通常是按utf8编码后写文件。
若是你用unicode类型写入,python会根据系统默认编码来把unicode编码成str再写入文件。由于写入文件须要的是str,是str就写,不是我就把你转成str再写。
简单原则,尽可能用str写入,避免使用默认编码,这样也不用在开头修改默认编码。
python中模块codecs中的open方法能够指定一个编码。它保证了读入和写出的字节都是按照这个指定编码进行编码的。
这样在读文件的时候:会把读出的str按照指定编码decode成unicode。
写文件的时候:若是是unicode,会根据指定编码encode成str而后写入;若是是str,会根据系统默认编码把str进行decode获得unicode,再根据指定编码encode成str进行写入。
简单原则,尽可能用unicode写入,避免使用默认编码,这样也不用在开头修改默认编码。
注意一下,对于其它方式读写文件,须要自行debugger看看编码的问题。好比我在python中读取excel的时候读出来就直接是unicode而不是str。
8. 通常的处理要点
(1) 首先把源文件的默认encoding和系统默认编码改成utf8
(2) 程序执行过程统一使用unicode类型
(3) 对于读写文件(用python内置的默认open来讲),获得的是str,对str进行相应的encode和decode就能够了。
总结一下就是:
设置相应的默认编码为utf8;
读文件拿到str类型:str -> decode('utf8') -> unicode
程序处理:用unicode
写文件:unicode -> encode('utf8') -> str,用str类型写入文件
固然前提是文件都是utf8格式的啦,包括源文件和读写的数据文件。
另外想说一下:
对于写程序的过程当中统一使用unicode类型这一点只是一个建议,由于统一unicode能够在处理字符串的时候减小麻烦。
以为所有弄成unicode麻烦的,能够考虑平时统一用utf8编码的str,有些问题须要用unicode的再转为unicode,遇到编码问题时能够思考是否是没有统一用unicode的问题(本文开头就给出了一个须要统一用unicode的状况)
其实弄清楚上面的思路,遇到什么编码问题也可以查错。