最近在用Python开发的时候,遇到些对中文数据的处理,报出了如下错误:
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe9 in position 0: ordinal not in range(128)
之前也遇到过,但是没有深入的去了解和测试,今天借此问题,对python的编码问题做个详细的学习;首先说明一点的是,目前公司的开发环境是Python 2.7;
从报错信息看,应该是编码的问题导致的,默认情况下,Python采用的是ascii编码方式,如下所示:
python -c "import sys; print sys.getdefaultencoding()" ascii
查询网上博客,也发现很多人在说是编码的问题:“Python在进行编码方式之间的转换时,会将 unicode 作为“中间编码”,但 unicode 最大只有 128 那么长,所以这里当尝试将 ascii 编码字符串转换成”中间编码” unicode 时由于超出了其范围,就报出了如上错误。”
同时网上的很多解决方法是在python文件的前面加上如下代码:
import sys defaultencoding = '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,如下所示:
>>> import sys >>> sys.getdefaultencoding() 'utf-8'
下面我们来做一个简单的测试:
# -*- coding: utf-8 -*- s = '中文' # 注意这里的 str 是 str 类型的,而不是 unicode s.encode('gb18030')
执行结果
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 的编码方式
# -*- coding: utf-8 -*- s = '中文' s.decode('utf-8').encode('gb18030')
第二种:更改sys.defaultencoding为文件的编码方式
# -*- coding: utf-8 -*- import sys #要重新载入sys。因为 Python 初始化后会删除 sys.setdefaultencoding 这个方法 reload(sys) sys.setdefaultencoding('utf-8')
结合上述测试的小案例,那么针对这种情况下,最后给出两种终极解决办法
1)第一种:这里我们将Python的默认编码方式修改为utf-8,就可以规避上述问题的发生,具体方式,我们在Python文件的前面加上如下代码:
import sys defaultencoding = 'utf-8' if sys.getdefaultencoding() != defaultencoding: reload(sys) sys.setdefaultencoding(defaultencoding)
2)第二种:我们在Python安装目录下的site-packages目录下添加一个sitecustomize.py文件,内容如下:
import sys sys.setdefaultencoding('utf-8')
这种方式可以解决所有项目的encoding问题,具体说明可参考Python安装目录下的site.py文件;
参考链接:
https://blog.csdn.net/l1028386804/article/details/78976807