https://www.cnblogs.com/liaohuiqiang/p/7247393.html
文章目录
1 ascii、unicode、utf8
1.1 ascii
最早的编码,只有127
个字符,包含英文字母,数字,标点符号和一些其它符号。一个字节表示一个字符。
1.2 unicode(统一码)
一个字节不够放,全世界有各种语言的字符需要编码,于是unicode给所有的字符都设定了唯一编码。通常都是用两个字节表示一个字符(有些生僻的字要用四个字节)。所以,要理解一点:下文中提到到的unicode
编码是双字节编码(一个字符两个字节)。
1.3 uft8
对于ascii
编码的那些字符,只需要1
个字节,unicode
给这些字符也设定2
个字节,如果一篇文章全是英文(ascii
字符),就浪费了很多空间(本来1
个字节可以存储的,用了2
个字节),所以产生了utf8
。utf8
是一种变长的编码方式,根据不同的符号变化字节长度,把ascii
编码成1
个字节,汉字通常编码成3
个字节,一些生僻的字符编码成4~6
个字节。
在计算机内存中,统一使用unicode
编码。
在python
中,建议程序过程中统一使用unicode
编码,保存文件和读取文件时使用utf8
(在读写磁盘文件时候用utf8
进行相应的decode
和encode
)。
2 encoding声明
python
默认使用ascii
编码去解释源文件。
如果源文件中出现了非ASCII
码字符,不在开头声明encoding
会报错。
可以声明为utf8
,告诉解释器用utf8
去读取文件代码,这个时候源文件有中文也不会报错。
# encoding=utf8 如果不加这一行会报错
print '解释器用相应的encoding去解释python代码'
3 python2.7中的str和unicode
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
模块。
6 查看文件编码
import chardet
with open(filename,'r') as f:
data = f.read()
return chardet.detect(data)
7 文件读写
7.1 open
首先要记住,读出和写入,这两个文件的关口都是用str
类型的,就是一个个字节。
python
中内置的默认open
在读取文件的时候以字节串str的形式,读出一个个字节。读取后要用正确的编码才能decode
成正确的unicode,所以要知道原来在文件中的编码。
写文件的时候也是一个道理,用str
类型,以字节的形式写入,这个str
是以某种编码方式编码的,要注意用正确的编码方式编码,一般是按utf8
编码后写文件。
如果你用unicode
类型写入,python
会根据系统默认编码来把unicode
编码成str再写入文件。因为写入文件需要的是str
,是str
就写,不是我就把你转成str
再写。
简单原则,尽量用str
写入,避免使用默认编码,这样也不用在开头修改默认编码。
7.2 codecs open
python中模块codecs
中的open
方法可以指定一个编码。它保证了读入和写出的字节都是按照这个指定编码进行编码的。
这样在读文件的时候:会把读出的str
按照指定编码decode
成unicode
。
写文件的时候:如果是unicode
,会根据指定编码encode
成str
然后写入;如果是str
,会根据系统默认编码把str
进行decode
得到unicode
,再根据指定编码encode
成str
进行写入。
简单原则,尽量用unicode
写入,避免使用默认编码,这样也不用在开头修改默认编码。
注意一下,对于其它方式读写文件,需要自行debugger看看编码的问题。比如我在python中读取excel的时候读出来就直接是unicode而不是str。
8 一般的处理要点
- 首先把源文件的默认
encoding
和系统默认编码改为utf8
- 程序执行过程统一使用
unicode
类型 - 对于读写文件(用
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
的情况)