欢迎各位兄弟 发布技术文章

这里的技术是共享的

You are here

python 乱码

shiping1 的头像

在本文中,以'哈'来解释作示例解释所有的问题,“哈”的各种编码如下: 
1. UNICODE (UTF8-16),C854; 
2. UTF-8,E59388; 
3. GBK,B9FE。 
一、python中的str和unicode 
一直以来,python中的中文编码就是一个极为头大的问题,经常抛出编码转换的异常,python中的str和unicode到底是一个什么东西呢? 
在python中提到unicode,一般指的是unicode对象,例如'哈哈'的unicode对象为 
u'\u54c8\u54c8' 
而str,是一个字节数组,这个字节数组表示的是对unicode对象编码(可以是utf-8、gbk、cp936、GB2312)后的存储的格式。这里它仅仅是一个字节流,没有其它的含义,如果你想使这个字节流显示的内容有意义,就必须用正确的编码格式,解码显示。 
例如:
python 字符串和unicode

对于unicode对象哈哈进行编码,编码成一个utf-8编码的str-s_utf8,s_utf8就是是一个字节数组,存放的就是'\xe5\x93\x88\xe5\x93\x88',但是这仅仅是一个字节数组,如果你想将它通过print语句输出成哈哈,那你就失望了,为什么呢?

因为print语句它的实现是将要输出的内容传送了操作系统,操作系统会根据系统的编码对输入的字节流进行编码,这就解释了为什么utf-8格式的字符串“哈哈”,输出的是“鍝堝搱”,因为 '\xe5\x93\x88\xe5\x93\x88'用GB2312去解释,其显示的出来就是“鍝堝搱”。这里再强调一下,str记录的是字节数组,只是某种编码的存储格式,至于输出到文件或是打印出来是什么格式,完全取决于其解码的编码将它解码成什么样子。

这里再对print进行一点补充说明:当将一个unicode对象传给print时,在内部会将该unicode对象进行一次转换,转换成本地的默认编码(这仅是个人猜测)

二、str和unicode对象的转换

str和unicode对象的转换,通过encode和decode实现,具体使用如下:

decode和encode演示

将GBK'哈哈'转换成unicode,然后再转换成UTF8

三、Setdefaultencoding

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 这个方法,我们需要重新载入

四、操作不同文件的编码格式的文件

建立一个文件test.txt,文件格式用ANSI,内容为:

abc中文

用python来读取

# coding=gbk

print open("Test.txt").read()

结果:abc中文

把文件格式改成UTF-8:

结果:abc涓枃

显然,这里需要解码:

# coding=gbk

import codecs

print open("Test.txt").read().decode("utf-8")

结果:abc中文

上面的test.txt我是用Editplus来编辑的,但当我用Windows自带的记事本编辑并存成UTF-8格式时,

运行时报错:

Traceback (most recent call last):

File "ChineseTest.py", line 3, in 

print open("Test.txt").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("Test.txt").read()

if data[:3] == codecs.BOM_UTF8:

data = data[3:]

print data.decode("utf-8")

结果:abc中文

五、文件的编码格式和编码声明的作用

源文件的编码格式对字符串的声明有什么作用呢?这个问题困扰一直困扰了我好久,现在终于有点眉目了,文件的编码格式决定了在该源文件中声明的字符串的编码格式,例如:

str = '哈哈'

print repr(str)

a.如果文件格式为utf-8,则str的值为:'\xe5\x93\x88\xe5\x93\x88'(哈哈的utf-8编码)

b.如果文件格式为gbk,则str的值为:'\xb9\xfe\xb9\xfe'(哈哈的gbk编码)

在第一节已经说过,python中的字符串,只是一个字节数组,所以当把a情况的str输出到gbk编码的控制台时,就将显示为乱码:鍝堝搱;而当把b情况下的str输出utf-8编码的控制台时,也将显示乱码的问题,是什么也没有,也许'\xb9\xfe\xb9\xfe'用utf-8解码显示,就是空白吧。>_<

说完文件格式,现在来谈谈编码声明的作用吧,每个文件在最上面的地方,都会用# coding=gbk 类似的语句声明一下编码,但是这个声明到底有什么用呢?到止前为止,我觉得它的作用也就是三个:

 

  1. 声明源文件中将出现非ascii编码,通常也就是中文;
  2. 在高级的IDE中,IDE会将你的文件格式保存成你指定编码格式。
  3. 决定源码中类似于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) 获取'哈哈'的编码:由文件编码格式确定,为'\xe5\x93\x88\xe5\x93\x88'(哈哈的utf-8编码形式)

2) 转成 unicode编码的时候,在这个转换的过程中,对于'\xe5\x93\x88\xe5\x93\x88'的解码,不是用utf-8解码,而是用声明编码处指定的编码GBK,将'\xe5\x93\x88\xe5\x93\x88'按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的编码'\xb9\xfe\xb9\xfe',当进行第二步,转换成 unicode的时候,会用UTF8对'\xb9\xfe\xb9\xfe'进行解码,而大家查utf-8的编码表会发现,utf8编码表(关于UTF- 8解释可参见字符编码笔记:ASCII、UTF-8、UNICODE)中根本不存在,所以会报上述错误。
来自 http://www.jb51.net/article/26543.htm

Python中文全攻略 中文乱码 输出中文乱码
插入数据库时,总是空,我做了如何操作,就好了。数据是采集过来的,程序是gbk编码person_sql="insert into analyst(education_id,alys_name,alys_sex,alys_img,alys_inte,person_id,alys_ctime,org_id) select * from (select (select education_id from education where education_name='"+personInfo[6].decode('gbk').encode('utf-8')+"'),'"+personInfo[1][0]+"','"+personInfo[2][0]+"','"+img+"','"+chenxin+"',convert('"+person_id+"' using gbk) as person_id,now(),(select org_id from organ where inst_code='"+inst_code+"' limit 1) org_id ) t where t.person_id not in(select person_id from analyst b where b.person_id=t.person_id);/n";

(转)解决UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 108: ordinal not in range(128)

python 2011-03-25 11:31:05 阅读21 评论0   字号: 订阅

混淆了 python2 里边的 str 和 unicode 数据类型。 

0. 

你需要的是让编码用实际编码而不是 ascii 

1. 
对需要 str->unicode 的代码,可以在前边写上 
import sys 
reload(sys) 
sys.setdefaultencoding('utf8') 
把 str 编码由 ascii 改为 utf8 (或 gb18030) 

2. 
python3 区分了 unicode str 和 byte arrary,并且默认编码不再是 ascii 

1.        在Python中使用中文
在Python中有两种默认的字符串:str和unicode。在Python中一定要注意区分“Unicode字符串”和“unicode对象”的区别。后面所有的“unicode字符串”指的都是python里的“unicode对象”。
事实上在Python中并没有“Unicode字符串”这样的东西,只有“unicode”对象。一个传统意义上的unicode字符串完全可以用str对象表示。只是这时候它仅仅是一个字节流,除非解码为unicode对象,没有任何实际的意义。
我们用“哈哈”在多个平台上测试,其中“哈”对应的不同编码是:
1.              UNICODE (UTF8-16),      C854;
2.              UTF-8,                    E59388;
3.              GBK,               B9FE。
1.1     Windows控制台
下面是在windows控制台的运行结果:
可以看出在控制台,中文字符的编码是GBK而不是UTF-16。将字符串s(GBK编码)使用decode进行解码后,可以得到同等的unicode对象。
注意:可以在控制台打印ss并不代表它可以直接被序列化,比如:
向文件直接输出ss会抛出同样的异常。在处理unicode中文字符串的时候,必须首先对它调用encode函数,转换成其它编码输出。这一点对各个环境都一样。
总结:在Python中,“str”对象就是一个字节数组,至于里面的内容是不是一个合法的字符串,以及这个字符串采用什么编码(gbk, utf-8, unicode)都不重要。这些内容需要用户自己记录和判断。这些的限制也同样适用于“unicode”对象。要记住“unicode”对象中的内容可绝对不一定就是合法的unicode字符串,我们很快就会看到这种情况。
总结:在windows的控制台上,支持gbk编码的str对象和unicode编码的unicode对象。
1.2     Windows IDLE(在Shell上运行)
在windows下的IDLE中,运行效果和windows控制台不完全一致:
可以看出,对于不使用“u”作标识的字符串,IDLE把其中的中文字符进行GBK编码。但是对于使用“u”的unicode字符串,IDLE居然一样是用了GBK编码,不同的是,这时候每一个字符都是unicode(对象)字符!!此时len(ss) = 4。
这样产生了一个神奇的问题,现在的ss无法在IDLE中正常显示。而且我也没有办法把ss转换成正常的编码!比如采用下面的方法:
这有可能是因为IDLE本地化做得不够好,对中文的支持有问题。建议在IDLE的SHELL中,不要使用u“中文”这种方式,因为这样得到的并不是你想要的东西。
这同时说明IDLE的Shell支持两种格式的中文字符串:GBK编码的“str”对象,和UNICODE编码的unicode对象。
1.3     在IDLE上运行代码
在IDLE的SHELL上运行文件,得到的又是不同的结果。文件的内容是:
直接运行的结果是:
毫无瑕疵,相当令人满意。我没有试过其它编码的文件是否能正常运行,但想来应该是不错的。
同样的代码在windows的控制台试演过,也没有任何问题。
1.4     Windows Eclipse
在Eclipse中处理中文更加困难,因为在Eclipse中,编写代码和运行代码属于不同的窗口,而且他们可以有不同的默认编码。对于如下代码:
#!/usr/bin/python
# -*- coding: utf-8 -*-
 
s = "哈哈"
ss = u'哈哈'
 
print repr(s)
print repr(ss)
 
print s.decode('utf-8').encode('gbk')
print ss.encode('gbk')
 
print s.decode('utf-8')
print ss
 
前四个print运行正常,最后两个print都会抛出异常:
'/xe5/x93/x88/xe5/x93/x88'
u'/u54c8/u54c8'
哈哈
哈哈
Traceback (most recent call last):
 File "E:/Workspace/Eclipse/TestPython/Test/test_encoding_2.py", line 13, in <module>
    print s.decode('utf-8')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
也就是说,GBK编码的str对象可以正常打印,但是不能打印UNICODE编码的unicode对象。在源文件上点击“Run as”“Run”,然后在弹出对话框中选择“Common”:
可以看出Eclipse控制台的缺省编码方式是GBK;所以不支持UNICODE也在情理之中。如果把文件中的coding修改成GBK,则可以直接打印GBK编码的str对象,比如s。
如果把源文件的编码设置成“UTF-8”,把控制台的编码也设置成“UTF-8”,按道理说打印的时候应该没有问题。但是实验表明,在打印UTF-8编码的str对象时,中文的最后一个字符会显示成乱码,无法正常阅读。不过我已经很满足了,至少人家没有抛异常不是:)
BTW: 使用的Eclipse版本是3.2.1。
1.5     从文件读取中文
在window下面用记事本编辑文件的时候,如果保存为UNICODE或UTF-8,分别会在文件的开头加上两个字节“/xFF/xFE”和三个字节“/xEF/xBB/xBF”。在读取的时候就可能会遇到问题,但是不同的环境对这几个多于字符的处理也不一样。
以windows下的控制台为例,用记事本保存三个不同版本的“哈哈”。
 

打开utf-8格式的文件并读取utf-8字符串后,解码变成unicode对象。但是会把附加的三个字符同样进行转换,变成一个unicode字符,字符的数据值为“/xFF/xFE”。这个字符不能被打印。编码的时候需要跳过这个字符。

打开unicode格式的文件后,得到的字符串正确。这时候适用utf-16解码,能得到正确的unicdoe对象,可以直接使用。多余的那个填充字符在进行转换时会被过滤掉。
 
打开ansi格式的文件后,没有填充字符,可以直接使用。
结论:读写使用python生成的文件没有任何问题,但是在处理由notepad生成的文本文件时,如果该文件可能是非ansi编码,需要考虑如何处理填充字符。
1.6     在数据库中使用中文
刚刚接触Python,我用的数据库是mysql。在执行插入、查找等操作时,如果运行环境使用的字符编码和mysql不一致,就可能导致运行时的错误。当然,和上面看到的情况一样,运行环境并不是关键因素,关键是查询语句的编码方式。如果在每次执行查询操作时都把查询字符串做一次编码转换,转变成mysql的默认字符编码,一样不会遇到问题。但是这样写代码也太痛苦了吧。
使用如下代码连接数据库:
self.conn = MySQLdb.connect(use_unicode = 1, charset='utf8', **server)
我不能理解的是既然数据库用的默认编码是UTF-8,我连接的时候也用的是UTF-8,为什么查询得到的文本内容却是UNICODE编码(unicode对象)?这是MySQLdb库的设置么?
1.7     在XML中使用中文
使用xml.dom.minidom和MySQLdb类似,对生成的dom对象调用toxml方法得到的是unicode对象。如果希望输出utf-8文本,有两种方法:
1.使用系统函数
在输出xml文档的时候进行编码,这是我觉得最好的方法。
xmldoc.toxml(encoding=’utf-8’)
xmldoc.writexml(outfile, encoding = ‘utf-8’)
2.自己编码生成
在使用toxml之后可以调用encode方法对文档进行编码。但这种方法无法得到合适的xml declaration(xml文档第一行中的encoding部分)。
不要尝试通过xmldoc.createProcessingInstruction来创建一个processing instraction:
<?xml version=’1.0’ encoding=’utf-8’?>
xml declaration虽然看起来像是,但是事实上并不是一个processing instraction。可以通下面的方法得到一个满意的xml文件:
print >> outfile, “<?xml version=’1.0’ encoding=’utf-8’?>”
print >> outfile, xmldoc.toxml().encode(‘utf-8’)[22:]
其中第二行需要过滤掉在调用xmldoc.toxml时生成的“<?xml version=’1.0’ ?>”,它的长度是22。
 
相面是两种方法的用法比较:
 
另外,在IDLE的shell中,不要用 u’中文’ 对属性进行赋值。上面讨论过,这样得到的unicode字符串不正确。

来自 http://blog.csdn.net/samxx8/article/details/6286407


 

普通分类: