python中⽂乱码问题深⼊分析
在本⽂中,以'哈'来解释作⽰例解释所有的问题,“哈”的各种编码如下:
1. UNICODE (UTF8-16),C854;
2. UTF-8,E59388;
3. GBK,B9FE。
⼀直以来,python中的中⽂编码就是⼀个极为头⼤的问题,经常抛出编码转换的异常,python中的str和unicode到底是⼀个什么东西呢?
在python中提到unicode,⼀般指的是unicode对象,例如'哈哈'的unicode对象为
u'\u54c8\u54c8'
⽽str,是⼀个字节数组,这个字节数组表⽰的是对unicode对象编码(可以是utf-8、gbk、cp936、GB2312)后的存储的格式。这⾥它仅仅是⼀个字节流,没有其它的含义,如果你想使这个字节流显⽰的内容有意义,就必须⽤正确的编码格式,解码显⽰。
例如:
对于unicode对象哈哈进⾏编码,编码成⼀个utf-8编码的str-s_utf8,s_utf8就是是⼀个字节数组,存放的就是' e5 93 88 e5 93 88',但是这仅仅是⼀个字节数组,如果你想将它通过print语句输出成哈哈,那你就失望了,为什么呢?
因为print语句它的实现是将要输出的内容传送了操作系统,操作系统会根据系统的编码对输⼊的字节流进⾏编码,这就解释了为什么utf-8格式的字符串“哈哈”,输出的是“鍝堝搱”,因为 ' e5 93 88 e5 93 88'⽤GB2312去解释,其显⽰的出来就是“鍝堝搱”。这⾥再强调⼀下,str记录的是字节数组,只是某种编码的存储格式,⾄于输出到⽂件或是打印出来是什么格式,完全取决于其解码的编码将它解码成什么样⼦。
这⾥再对print进⾏⼀点补充说明:当将⼀个unicode对象传给print时,在内部会将该unicode对象进⾏⼀次转换,转换成本地的默认编码(这仅是个⼈猜测)
⼆、str和unicode对象的转换
str和unicode对象的转换,通过encode和decode实现,具体使⽤如下:
将GBK'哈哈'转换成unicode,然后再转换成UTF8字符串转数组编码方式
三、Setdefaultencoding
如上图的演⽰代码所⽰:
当把s(gbk字符串)直接编码成utf-8的时候,将抛出异常,但是通过调⽤如下代码:
import sys
reload(sys)
sys.setdefaultencoding('gbk')
后就可以转换成功,为什么呢?在python中str和unicode在编码和解码过程中,如果将⼀个str直接编码成另⼀种编码,会先把str解码成unicode,采⽤的编码为默认编码,⼀般默认编码是anscii,所以在上⾯⽰例代码中第⼀次转换的时候会出错,当设定当前默认编码为'gbk'后,就不会出错了。
⾄于reload(sys)是因为Python2.5 初始化后会删除 sys.setdefaultencoding 这个⽅法,我们需要重新载⼊。
四、操作不同⽂件的编码格式的⽂件
建⽴⼀个⽂件,⽂件格式⽤ANSI,内容为:
abc中⽂
⽤python来读取
# coding=gbk
print open("").read()
结果:abc中⽂
把⽂件格式改成UTF-8:
结果:abc涓 枃
显然,这⾥需要解码:
# coding=gbk
import codecs
print open("").read().decode("utf-8")
结果:abc中⽂
上⾯的我是⽤Editplus来编辑的,但当我⽤Windows⾃带的记事本编辑并存成UTF-8格式时,
运⾏时报错:
Traceback (most recent call last):
File "ChineseTest.py", line 3, in
print open("").read().decode("utf-8")
UnicodeEncodeError: 'gbk' codec can't encode character u'\ufeff' in position 0: illegal multibyte sequence
原来,某些软件,如notepad,在保存⼀个以UTF-8编码的⽂件时,会在⽂件开始的地⽅插⼊三个不可见的字符(0xEF 0xBB 0xBF,即BOM)。
因此我们在读取时需要⾃⼰去掉这些字符,python中的codecs module定义了这个常量:
# coding=gbk
import codecs
data = open("").read()
if data[:3] == codecs.BOM_UTF8:
data = data[3:]
print data.decode("utf-8")
结果:abc中⽂
源⽂件的编码格式对字符串的声明有什么作⽤呢?这个问题困扰⼀直困扰了我好久,现在终于有点眉⽬了,⽂件的编码格式决定了在该源⽂件中声明的字符串的编码格式,例如:
str = '哈哈'
print repr(str)
a.如果⽂件格式为utf-8,则str的值为:' e5 93 88 e5 93 88'(哈哈的utf-8编码)
b.如果⽂件格式为gbk,则str的值为:' b9 fe b9 fe'(哈哈的gbk编码)
在第⼀节已经说过,python中的字符串,只是⼀个字节数组,所以当把a情况的str输出到gbk编码的控制台时,就将显⽰为乱码:鍝堝搱;⽽当把b情况下的str输出utf-8编码的控制台时,也将显⽰乱码的问题,是什么也没有,也许' b9 fe b9 fe'⽤utf-8解码显⽰,就是空⽩吧。>_<
说完⽂件格式,现在来谈谈编码声明的作⽤吧,每个⽂件在最上⾯的地⽅,都会⽤# coding=gbk 类似的语句声明⼀下编码,但是这个声明到底有什么⽤呢?到⽌前为⽌,我觉得它的作⽤也就是三个:
声明源⽂件中将出现⾮ascii编码,通常也就是中⽂;在⾼级的IDE中,IDE会将你的⽂件格式保存成你指定编码格式。决定源码中类似于u'哈'这类声明的将‘哈'解码成unicode所⽤的编码格式,也是⼀个⽐较容易让⼈迷惑的地⽅,看⽰例: #coding:gbk
ss = u'哈哈'
print repr(ss)
print 'ss:%s' % ss
将这个些代码保存成⼀个utf-8⽂本,运⾏,你认为会输出什么呢?⼤家第⼀感觉肯定输出的肯定是:
u'\u54c8\u54c8'
ss:哈哈
但是实际上输出是:
u'\u935d\u581d\u6431'
ss:鍝堝搱
为什么会这样,这时候,就是编码声明在作怪了,在运⾏ss = u'哈哈'的时候,整个过程可以分为以下⼏步:
1) 获取'哈哈'的编码:由⽂件编码格式确定,为' e5 93 88 e5 93 88'(哈哈的utf-8编码形式)
2) 转成 unicode编码的时候,在这个转换的过程中,对于' e5 93 88 e5 93 88'的解码,不是⽤utf-8解码,⽽是⽤声明编码处指定的编码GBK,将' e5 93 88 e5 93 88'按GBK解码,得到就是''鍝堝搱'',这三个字的unicode编码就是
u'\u935d\u581d\u6431',⾄⽌可以解释为什么print repr(ss)输出的是u'\u935d\u581d\u6431' 了。
好了,这⾥有点绕,我们来分析下⼀个⽰例:
#-*- coding:utf-8 -*-
ss = u'哈哈'
print repr(ss)
print 'ss:%s' % ss
将这个⽰例这次保存成GBK编码形式,运⾏结果,竟然是:
UnicodeDecodeError: 'utf8' codec can't decode byte 0xb9 in position 0: unexpected code byte
这⾥为什么会有utf8解码错误呢?想想上个⽰例也明⽩了,转换第⼀步,因为⽂件编码是GBK,得到的是'哈哈'编码是GBK的编码' b9 fe b9 fe',当进⾏第⼆步,转换成 unicode的时候,会⽤UTF8对' b9 fe b9 fe'进⾏解码,⽽⼤家查utf-8的编码表会发现,utf8编码表(关于UTF- 8解释可参见字符编码笔记:ASCII、UTF-8、UNICODE)中根本不存在,所以会报上述错误。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论