几个编码概念
-
脚本字符编码: 指定python脚本中字符的编码方式(python在处理数据时,以什么编码方式来处理)
- python2: ASCII
- python3: UTF-8
-
解释器字符编码: python解释器默认的编码方式
- python2: ASCII
- python3: UTF-8
-
系统编码: 这个编码和python本身没有太大关系,只是我们有时候会看到乱码情况,这时就和系统编码有关
几类常见的问题
-
SyntaxError: Non-ASCII character 'xe5' in file...
这一类问题一般出现在python2中,由
脚本字符编码
导致。当脚本中出现中文,且未指定编码类型,那么python2在处理脚本数据时,会用默认的ASCII去解析中文,导致上面的error。python2的脚本文件,一般都会说明字符编码类型,如
#-*- encoding: utf-8 -*-
-
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
这类问题一般出现在python2中,在编码类型转换中触发。
python2中有两个编解码函数:
decode
和encode
,用于unicode编码和其他编码方式的转换。decode
将其他编码类型的字符串,转化为unicode编码,反之,encode
将unicode编码转换为其他. 我们可以通过这两个函数,获取某种编码下的变量值,如:>>> import chardet >>> a = '张三' >>> chardet.detect(a) {'confidence': 0.7525, 'language': '', 'encoding': 'utf-8'} # 系统默认编码为utf-8, 所以a的编码为utf-8 >>> b = a.decode('utf-8') # 用utf-8 解码后,得到unicode字符串 >>> b u'u5f20u4e09' >>> c = b.encode('gb2312') # 将unicode 编码为gbk >>> chardet.detect(c) {'confidence': 0.682639754276994, 'language': 'Russian', 'encoding': 'KOI8-R'} >>> chardet.detect(a.decode('utf-8').encode('gb2312')) # 一次性变更有同样的效果 {'confidence': 0.682639754276994, 'language': 'Russian', 'encoding': 'KOI8-R'}
但是,当由于某种原因,我们无意将一种非unicode编码,转换为另一种非unicode编码,且未指定解码方式时,可能出现上述error,如
>>> a.encode('gb2312') # a是utf-8编码,强行转换成gb2312,会出错 Traceback (most recent call last): File "<stdin>", line 1, in <module> UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 0: ordinal not in range(128)
这是因为,如果a不是unicode编码,且要转换成另外一种编码的情况下,python会用默认的解释器编码先进行解码,即进行
a.decode('ascii')
的操作,但是由于a中包含中文,ascii无法解析,故报错 -
乱码问题。
乱码不是python自身的问题,而是和第三方
系统
相关,即变量的编码类型,和显示系统的编码类型不一致,会造成乱码。简单举个例子:此刻我使用的macos,系统默认编码为
utf-8
>>> a = '李四' >>> chardet.detect(a) {'confidence': 0.7525, 'language': '', 'encoding': 'utf-8'} >>> print(a) 李四 >>> b = a.decode('utf-8').encode('gbk') >>> print(b) ���� >>>
python3中对编码问题的优化
-
脚本中的字符不再受
系统默认编码
的影响,统一为unicode➜ ~ python2 Python 2.7.16 (default, Feb 29 2020, 01:55:37) [GCC 4.2.1 Compatible Apple LLVM 11.0.3 (clang-1103.0.29.20) (-macos10.15-objc- on darwin Type "help", "copyright", "credits" or "license" for more information. >>> >>> import chardet >>> a = '张三' >>> chardet.detect(a) {'confidence': 0.7525, 'language': '', 'encoding': 'utf-8'}
可以看到,在python2中,字符的默认编码是和
系统默认编码
保持一致的
特别注意: 字符默认编码和# -*- encoding=xxx -*-
的声明没有关系,该声明只是告诉python解释器应该用什么编码方式去读取脚本内容➜ ~ python3 Python 3.7.3 (default, Sep 18 2019, 14:29:06) [Clang 11.0.0 (clang-1100.0.33.8)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> >>> import chardet >>> a = '张三' >>> chardet.detect(a) # chardet的参数必须是字节流,python3中字符默认unicode,所以报错 Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/Library/Python/3.7/site-packages/chardet/__init__.py", line 34, in detect '{0}'.format(type(byte_str))) TypeError: Expected object of type bytes or bytearray, got: <class 'str'> >>> type(a) <class 'str'> # python3中使用str表示unicode
可以看到,python3中,字符的默认编码是str(也就是py2中的unicode),不再受
系统默认编码
影响 -
脚本默认编码方式为
utf-8
, 即不必在脚本开头声明# -*- encoding: utf-8 -*-
,也可以在脚本中随意使用中文 -
将字符串和字节序列做了区分。增加了bytes数据类型。
字符串str: 对标python2中的unicode
>>> a = '李四' >>> type(a) <class 'str'>
比如,a的默认类型即为str类
bytes: 对标python2中str类型(包含各种编码方式)
>>> b = a.encode('utf-8') >>> type(b) <class 'bytes'> >>> chardet.detect(b) {'encoding': 'utf-8', 'confidence': 0.7525, 'language': ''} >>> type(a.encode('gbk')) <class 'bytes'>