序:
总是有人问”python乱码”问题,在这里,我做一下解答,希望以后有遇到类似问题的人,能自行解决。
引子:
最近在学习写smtp+pop3客户端,由于发/收的邮件中的中文都是乱码,于是百度,看到上面这个截图,多次尝试,乱码问题解决,后面讲我的解决过程。
原理:
乱码根本原因在于“编码-解码不一致”
python中,我们使用decode()和encode()来进行解码和编码, 在python中,使用unicode类型作为编码的基础类型。即
显示数据两个来源:源代码引用的外部数据(socket,file,raw_input)、源代码内部的数据
数据源编码:是指源代码引用外部数据,外部数据编码需要与显示程序编码一致,例如源代码使用socket对网页抓包,网页数据即为数据源,而网页本身是有特定编码的。
源代码编码:python源代码默认使用ASCII编码,当部分字符无法ASCII编码时,默认使用用户申明的编码例如# encoding: utf-8,如果不申明,可能会因编辑器的不同,打开源代码出现乱码情况。源代码编码需要与显示程序编码一致。
显示编码:黑白显示器只能显示黑白,如果输入彩色信号也是如此,显示输出的程序对编码同样有要求,要显示的数据需要与显示程序要求的编码一致。
常见显示编码:在windows中,如果双击python***.py文件,则默认调用python shell解释,并使用windows cmd shell(cp936编码)程序做输出,在linux终端SecureCRT显示数据通常设置使用utf-8编码,python自带的IDLE在windows下默认使用cp936编码。
编码一致:通过上面描述我们知道【显示数据编码需要与显示数据程序编码一致】,那么如何一致呢?需要通过中间编码unicode实现一致:源编码—>unicodeà—>目标编码
例如网页常使用gb2312编码(数据源utf-8编码暂不考虑),*.py源代码通常使用utf-8编码,显示端初学者学用windows cmd 的cp936编码,要保持一致可如此修改print‘string’语句: (string来自网页) print‘string’.decode(‘GB2312’).encode(‘cp936’).
其中‘string’.decode(‘GB2312’)à变为unicode,而*.encode(‘cp936’)则让unicode编码à显示端的cp936编码。
解决思路:
查看编码信息:
邮件编码
我使用poplib抓取邮件信息,了解到邮件使用GB2312编码
网页编码
想要对网页数据抓取的朋友,必须对网页的编码信息准确获取。对目标网站源代码做如下搜索即可发现编码方式:
<meta http-equiv="content-type" content="text/html;charset=utf-8">
数据传输编码
正确解码:
编码与解码关系
一个比喻:编码是如何保存的问题,解码是如何显示的问题。
Window编码解码流程:
源代码解码:
ASCII/utf-8 ->unicode(str. decode('utf-8'))->cp936(str. encode('cp936'))
数据类型:ASCII/utf-8(<type ‘str’>) ;unicode(<type ‘unicode’>) ;cp936(<type ‘str’>)
cp936为unicode是中文子集,因此unicode已经可以在windows下正确显示。
源代码引用外部数据解码:
假设外部数据使用xx编码:xx àunicodeàcp936
python中的编码与解码
Python 源代码默认编码为ASCII码,在源代码申明utf-8后,发生了编码过程非ASCII字符àutf-8.
先说一下python中的字符串类型,在python中有两种字符串类型,分别是str和unicode,他们都是basestring的派生类;str类型是一个包含Characters represent (at least) 8-bit bytes的序列;unicode的每个unit是一个unicode obj;所以:
len(u'中国')的值是2;len('ab')的值也是2;
在str的文档中有这样的一句话:The string data type is also used to represent arrays of bytes, e.g., to hold data read from a file. 也就是说在读取一个文件的内容,或者从网络上读取到内容时,保持的对象为str类型;如果想把一个str转换成特定编码类型,需要把str转为Unicode,然后从unicode转为特定的编码类型如:utf-8、gb2312等;(当编解码不一致时,通常用unicode作为中转)
一些实例:
eg1. 源代码编码
此例中变量s,与print s的显示环境linux bash shell都使用相同的编码utf-8编码,因此没有出现乱码。
编程过程:utf-8 /ASCIIàpython shell传递给linux bash shell(使用utf-8解码)显示
注意unicode(变量,’utf-8) / u”string”与默认ASCII编码的区别
eg2.将eg1中的代码使用windows环境运行,结果出现乱码,这是因为,变量s为ASCII编码,而windows cmd shell作为显示程序 使用cp936编码显示,当然乱码
编码过程:utf-8/ASCIIàcmd shell(cp936解码)显示
解决办法,让输出的ASCII/utf-8编码转码为cp936编码,乱码解决
eg 3.邮件编码(数据源编码)
翻阅历史邮件,查询编码方式
正确解码:GB2312à’string’.decode(‘GB2312’)àunicodeàutf-8
运行程序
技巧与方法:
sys.defaultencoding模块
Python 使用sys.defaultencoding 的默认编码ASCII编码解码,但变量s的编码为utf-8编码,因此报错。
解决办法一:明确指定解码方式
解决办法 二:设置默认解码方式sys.setdefaultencoding('utf-8')
codecs.open()方法
用于解决文件读写时的编码问题
Python3的编码
在新版本的python3中,取消了unicode类型,代替它的是使用unicode字符的字符串类型(str),字符串类型(str)成为基础类型如下所示,而编码后的变为了字节类型(bytes)但是两个函数的使用方法不变:
decode encode
bytes ------> str(unicode)------>bytes
u = '中文' #指定字符串类型对象u,默认为unicode编码
str = u.encode('gb2312') #以gb2312编码对u进行编码,获得bytes类型对象str
u1 = str.decode('gb2312')#以gb2312编码对字符串str进行解码,获得字符串类型对象u1
u2 = str.decode('utf-8')#如果以utf-8的编码对str进行解码得到的结果,将无法还原原来的字
参考文章:
http://blog.csdn.net/turkeyzhou/article/details/8927361
http://www.cnblogs.com/zhaoyl/p/3770340.html
http://www.pythonclub.org/python-basic/encode-detail
http://wolfmaster.iteye.com/blog/638029
http://blog.chinaunix.net/uid-517401-id-353375.html