这是编码系列最后一篇, 只讲有关 python2 的部分
python2 与 python3 相比有些设定引起了很多莫名其妙的编码问题, 我们通常诟病 python2 在编码方面的坑其实指的就是这部分莫名其妙的问题
我们通常说的正常的编码问题是上文提到的 python3 中会遇到的编码问题
文件或网页的编码方式和读取时使用的方式不同, 这是非常正常的, 解决思路也非常清晰, 只要使用正确的编码就可以了
用 encode 和 decode 方法编码和解码时, 使用错误编码造成的错误, 这也是找到正确的编码就可以解决
而说 python2 中的很多编码问题莫名其妙, 是因为这些编码解码不是人为指定的, 而是经常在你不知道的时候, 程序就按照某种编码方式对某些字符进行了编码或解码, 而它使用的这种编码方式还不正确, 导致了报错或乱码此时使用者会觉得自己根本没有做编码或解码的工作怎么就会发生编码方面的错误下面就来大致讲一讲这些问题出现的原因, 相信看完上面的基础, 理解 python2 中的问题是非常容易的
这些莫名其妙的编码问题主要是由两个默认编码造成的
py2 中默认编码方式是 ASCII,py3 是 UTF-8(这个默认编码在什么时候会遇到后面会讲), 这个是引起编码问题的主要原因
py2 中用 a='中文'定义的字符串其实不是真正的字符串, 它不是 Unicode, 所以说在定义 a 时默认进行了一次编码转换, 这个编码转换和上面的 ASCII 还没有关系, 使用的是另一种默认的编码
上那两个默认编码在 python3 中几乎不会用到, 它们产生的原因在于 pyhton2 设计上的缺陷, 下面就列一下 python2 是如何导致的编码错误, 一共 5 种错误
1. 定义 str 时的自动编码
在 python2 中, a='中文'这个 a 不是 Unicode,b=u'中文'这个 b 才是而在 python3 中根本没有 u'中文'这一说, a='中文'这个 a 就是 Unicode
准确地说, a 叫字节串, b 才叫字符串(前面提到过 Unicode 和字符串完全等同)a 其实是一个二进制字节流, 是字符串 encode 后的产物所以说在 a='中文'这个赋值过程中, 默认进行了一次编码 encode, 将中文这个字符串编码成了二进制数
既然是编码, 肯定是按照某一套编码方式来做的这个编码方式再不同场景是不一样的用下面代码查看当前默认编码是什么
- import locale
- locale.getdefaultlocale()
这就是两种默认编码中的第二种
一般在 windows 命令行下结果是('zh_CN', 'cp936'), 即使设置了 chcp 65001, 或者在 jupyter 中使用也是一样是 cp936
说明定义字符串 (准确来说应该叫字节串) 时, 在 windows 下都会默认用 GBK 进行编码
字符串的两种定义方式会让使用者产生混乱, 更容易触犯到后面的那些雷区
解码时如果不能理解它原来是怎么编码的就会使用错误的编码从而造成报错
2.str 和 unicode 混用造成的错误
由于第一条中的差异, a='中文'这样定义的 a 在编码和解码方面也与 py3 是有不同的
在 python3 中, 这样定义的 a 只能 encode, 因为它是 Unicode
在 python2 中, 这个 a 主要是要 decode, 因为它是编码过的
从这一点中, 第一种默认编码上场了
如果 str 和 unicode 混用, 比如'中文'+u'好', 这个过程会默认将 str 解码成 unicode, 使用的是 py2 默认的 ASCII 编码, 而中文不对应 ASCII 编码, 于是报错这是第一种默认编码产生错误的第一种, 之后还有很多地方都会有类似的默认转换
在这里首先说一下如何查看这个默认编码是什么
- import sys
- sys.getdefaultencoding()
就是这个命令, 在 py2 中结果是 ascii, 在 py3 中就是 utf-8 涉及到的两个默认编码都出现了, 查看命令也不一样, 注意区分
另外再说一下关于这点 python3 是怎么设计的如果 str 和 bytes 直接相加, 会直接报出一个错误
TypeError: Can't convert'bytes' object to str implicitly
,python3 说的很明确不能隐式转, 而不是像 python2 一样偷偷给你转了
3.py2 编码解码通用不规范造成的错误
这里要列两点 py2 和 py3 的差别
py3 中的两个 type:str 和 bytes 分别对应 py2 中的 unicode 和 strpython2 中, a='中文'这个 a 就是 str 类, b=u'中文'这个 b 就是 unicode 类
在 py2 中, 无论 unicode 类还是 str 类, 都可以使用 encode 和 decode 方法, 而 py3 中明确 str 只能使用 encode,bytes 只能使用 decode
在 py2 中, 按理说 str 应该只能 decode, 而 unicode 应该只能 encode, 而现在开放了全都可以, 让使用者更搞不清楚到底应该 decode 还是 encode, 如果用错了会出现如下错误
- UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
- UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
其中第一条是将 unicode 强行 decode 的报错, 第二条是将 str 强行 encode 的报错对于这两种强行解码编码的内部运行机制是
unicode 强行 decode 时, 因为 unicode 确实不能 decode, 于是 py2 会默认将 unicode 用 ascii encode 一下, 再用得到的结果去 decode, 就在 encode 的过程中就报错了
str 强行 decode 时, 因为 str 确实不能 encode, 于是 py2 会默认将 str 用 ascii decode 一下, 再用得到的结果去 encode, 就在 decode 的过程中就报错了
这是第一种默认编码产生错误的第二种情况
4.print 时的默认编码
我们之前提到过, a='中文'这样定义的 a 其实本身是字节串而不是字符串, 在控制台中输入 a 的输出结果和输入 print(a)的输出结果是不一样的
输入 a 的输出结果是 16 进制形式的, 也就是它的本来面貌
输入 print(a)的输出结果是我们能看懂的字符中文
说明在 print 时其实进行了一次默认解码, 将字节转换成了 unicode 呈现出来这时 print 的是 str 类型, 如果传入的是 unicode 类型, 又会自动按照 ascii 编码将 unicode encode 为 str, 然后再 print 出来, 如下所示
- b = u'中文'
- print(b)
- # UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
这时第一种默认编码产生错误的第三种情况
5. 函数参数类型传入错误
函数参数本应传入 str 类型, 而传入的是 unicode 类型的时候, 也会自动使用 ascii 编码 encode
这个其实可以算是第 4 条 print 的推广, str 和 raw_input 函数都是这样
- b = u'中文'
- print(b)
- str(b)
- a = raw_input(b)
后三行分别指定都会报错, 也是 encode 用 ascii 编码的问题, 这时第一种默认编码产生错误的更多情况
python2 中的编码问题参考三篇非常好的文章
Python 编码错误的本质原因
Python 2.x 字符编码终极指南
熟悉又陌生的字符编码
因为我的电脑里没有装 python2, 一切测试结果都在这个网站中进行
专栏信息
专栏主页: python 编程
专栏目录: 目录
版本说明: 软件及包版本说明
来源: https://juejin.im/entry/5aa736c6f265da239c7b051d