问题
在平时工作中,遇到了这样的错误:
1
|
UnicodeDecodeError: 'ascii' codec can't decode byte |
想必大家也都碰到过,很常见 。于是决定对python的编码做一个整理和学习。
基础知识
在python2.x中,有两种数据类型,unicode和str,这两个都是basestring的子类
1
2
3
4
5
6
7
8
9
10
|
>>> a = '中' >>> type (a) < type 'str' > >>> isinstance (a, basestring ) True >>> a = u '中' >>> type (a) < type 'unicode' > >>> isinstance (a, basestring ) True |
两者的区别,概括来讲,str是字节串,由unicode经过编码(encode)后的字节组成的(好比与python3.x的byte);unicode是对象,才是真正意义上的字符串,由字符组成
1
2
3
4
5
6
7
8
9
10
|
>>> a = '中文' >>> len (a) 6 >>> repr (a) "'\\xe4\\xb8\\xad\\xe6\\x96\\x87'" >>> b = u '中文' >>> len (b) 2 >>> repr (b) "u'\\u4e2d\\u6587'" |
控制台和脚本
在linux下的python控制台执行以下命令,所得的结果和执行脚本是不同的
1
2
3
4
5
6
|
>>> a = u '中文' >>> repr (a) "u'\\xe4\\xb8\\xad\\xe6\\x96\\x87'" >>> b = unicode ( '中文' , 'utf-8' )b) >>> repr (b) "u'\\u4e2d\\u6587'" |
可以看到,u'中文'初始化的对象a不是我们所期望的,那究竟是什么原因呢?
将python看成是一根管子,管子里头处理的中间过程都是使用unicode的。入口处,全部转成unicode;出口处,再转成目标编码(当然,有例外,处理逻辑中要用到具体编码的情况)。
在控制台执行命令a = u'中文',可以将解释为命令,a = ‘中文'.decode(encode),从而到到unicode对象a。那么这里的encode是什么呢?对于控制台来说,就是标准输入,即sys.stdin.encoding
1
2
|
>>> sys.stdin.encoding 'ISO-8859-1' |
我的这边控制台默认的编码是ISO-8859-1,故a = u'中文' <=> a = '中文'.decode('ISO-8859-1')
这里的'中文'是控制台理解的,即使根据终端编码方式编码后的字节码,对于utf-8编码的终端,'中文'='\\xe4\\xb8\\xad\\xe6\\x96\\x87'
1
2
3
|
>>> a = '中文' .decode( 'ISO-8859-1' ) >>> repr (a) "u'\\xe4\\xb8\\xad\\xe6\\x96\\x87'" |
那如何修改此编码值呢,设置为什么呢?在linux环境中设置环境变量方法如下,具体设置什么只要与终端编码方式一直即可
1
|
export PYTHONIOENCODING = UTF - 8 |
总结
重新回到最初的那个问题,造成问题的原因是没有搞清楚unicode和str的区别,将两者进行了混用。
1
2
3
4
5
|
>>> a = '中文' >>> a.encode( 'gbk' ) Traceback (most recent call last): File "<stdin>" , line 1 , in <module> UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0 : ordinal not in range ( 128 ) |
以上的对象a其实是str,即字节码,若终端是utf-8编码的话,那么a就是用utf-8 encode的字节码。a.encode('gbk') 等价于a.decode(encoding).encode('gbk'),即先将字节码解码为unicode字符,然后再encode为字节码。unicode对象作为中转站。那么这里的encoding是什么呢?
1
2
3
|
>>> import sys >>> sys.getdefaultencoding() 'ascii' |
默认是ascii,这正是错误为什么报无法用ascii解码的原因
1
2
3
4
5
6
7
8
|
>>> reload (sys) <module 'sys' (built - in )> >>> sys.setdefaultencoding( 'utf-8' ) >>> a = '中文' >>> repr (a) "'\\xe4\\xb8\\xad\\xe6\\x96\\x87'" >>> a.encode( 'gbk' ) '\xd6\xd0\xce\xc4' |
将默认编码改为utf-8,即可。不鼓励对str使用encode方法,因为其中隐式对str进行了解码。decode只对str,encode只对unicode,一切decode/encode都显示指定编码方式。