Python中编码问题(UnicodeDecodeError)的处理

2021-03-18 16:27:24 浏览数 (1)

最近在用Python开发的时候,遇到些对中文数据的处理,报出了如下错误:

代码语言:javascript复制
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in position 0: ordinal not in range(128)

之前也遇到过,但是没有深入的去了解和测试,今天借此问题,对python的编码问题做个详细的学习;首先说明一点的是,目前公司的开发环境是Python 2.7;

从报错信息看,应该是编码的问题导致的,默认情况下,Python采用的是ascii编码方式,如下所示:

代码语言:javascript复制
python -c "import sys; print sys.getdefaultencoding()"ascii

查询网上博客,也发现很多人在说是编码的问题:“Python在进行编码方式之间的转换时,会将 unicode 作为“中间编码”,但 unicode 最大只有 128 那么长,所以这里当尝试将 ascii 编码字符串转换成”中间编码” unicode 时由于超出了其范围,就报出了如上错误。”

同时网上的很多解决方法是在python文件的前面加上如下代码:

代码语言:javascript复制
import sysdefaultencoding = 'utf-8'if sys.getdefaultencoding() != defaultencoding:    reload(sys)    sys.setdefaultencoding(defaultencoding)

然后,根据网上给出的解决方法,在代码最开始添加上述代码,发现问题解决了;但是,经测试发现,这种方法仅适用于python2.7,在python3中不适用,因为python3已经取消了reload,而在对于编码方法做了很大的调整

Python3 最重要的一项改进之一就是解决了 Python2 中字符串与字符编码遗留下来的这个大坑。

Python2 字符串设计上的一些缺陷:

使用 ASCII 码作为默认编码方式,对中文处理很不友好。 把字符串的牵强地分为 unicode 和 str 两种类型,误导开发者

当然这并不算 Bug,只要处理的时候多留心也可以避免这些坑。但在 Python3 两个问题都很好的解决了。

首先,Python3 把系统默认编码设置为 UTF-8,如下所示:

代码语言:javascript复制
>>> import sys>>> sys.getdefaultencoding()'utf-8'

下面我们来做一个简单的测试:

代码语言:javascript复制
# -*- coding: utf-8 -*-   s = '中文'  # 注意这里的 str 是 str 类型的,而不是 unicode   s.encode('gb18030'

执行结果

代码语言:javascript复制
# python t1.py Traceback (most recent call last):  File "t1.py", line 3, in     s.encode('gb18030')   UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)

上述代码将 s 重新编码为 gb18030的格式,即进行unicode -> string的转换。因为 s 本身就是 string类型的,因此 Python 会自动的先将 s 解码为 unicode ,然后再编码成 gb18030。因为解码是python自动进行的,我们没有指明解码方式,python 就会使用 sys.defaultencoding指明的方式来解码。python2.7环境下 sys.defaultencoding 是 anscii,如果 s 不是这个类型就会出错。

拿上面的情况来说,我的 sys.defaultencoding是anscii,而 s 的编码方式和文件的编码方式一致,是 utf8 的,所以出错了: __UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe4 in position 0: ordinal not in range(128) __

Python 里面的编码和解码也就是 unicode 和 string 这两种形式的相互转化.

编码是 unicode -> str,相反的,解码就 是 str -> unicode

剩下的问题就是确定何时需要进行编码或者解码了.

关于文件开头的”编码指示”,也就是 # -*- coding: -*- 这个语句。Python 默认脚本文件都是 UTF-8 编码的,当文件中有非 UTF-8 编码范围内的字符的时候就要使用”编码指示”来修正. 关于 sys.defaultencoding,这个在解码没有明确指明解码方式的时候使用。比如上述简单的小测试

错误之前在解决,首先要了解unicode和utf-8的区别。

python中字符串类型分为byte string 和unicode string两种。

如果在python文件中指定编码方式为utf-8(#coding=utf-8),那么所有带中文的字符串都会被认为是utf-8编码的byte string(例如:mystr=”你好,李焕英”),但是在函数中所产生的字符串则被认为是unicode string

问题就出在这边,unicode string 和byte string是不可以混合使用的,一旦混合使用了,就会产生这样的错误。例如:

self.response.out.write(“你好,李焕英” self.request.get(“args”))

其中,”你好,李焕英”被认为是byte string,而self.request.get(“args”)的返回值被认为是unicode string。由于预设的解码器是ascii,所以就不能识别中文byte string。然后就报错了。

以下有两个解决方法:

第一种:明确的指示出 s 的编码方式

代码语言:javascript复制
# -*- coding: utf-8 -*- s = '中文' s.decode('utf-8').encode('gb18030')

第二种:更改sys.defaultencoding为文件的编码方式

代码语言:javascript复制
# -*- coding: utf-8 -*- import sys     #要重新载入sys。因为 Python 初始化后会删除 sys.setdefaultencoding 这个方法reload(sys) sys.setdefaultencoding('utf-8')

结合上述测试的小案例,那么针对这种情况下,最后给出两种终极解决办法

1)第一种:这里我们将Python的默认编码方式修改为utf-8,就可以规避上述问题的发生,具体方式,我们在Python文件的前面加上如下代码:

代码语言:javascript复制
import sysdefaultencoding = 'utf-8'if sys.getdefaultencoding() != defaultencoding:    reload(sys)    sys.setdefaultencoding(defaultencoding)

2)第二种:我们在Python安装目录下的site-packages目录下添加一个sitecustomize.py文件,内容如下:

代码语言:javascript复制
import syssys.setdefaultencoding('utf-8')

这种方式可以解决所有项目的encoding问题,具体说明可参考Python安装目录下的site.py文件;

0 人点赞