python编码问题记录

几个编码概念

  1. 脚本字符编码: 指定python脚本中字符的编码方式(python在处理数据时,以什么编码方式来处理)

    • python2: ASCII
    • python3: UTF-8
  2. 解释器字符编码: python解释器默认的编码方式

    • python2: ASCII
    • python3: UTF-8
  3. 系统编码: 这个编码和python本身没有太大关系,只是我们有时候会看到乱码情况,这时就和系统编码有关

几类常见的问题

  1. SyntaxError: Non-ASCII character ‘\xe5‘ in file...

    这一类问题一般出现在python2中,由脚本字符编码导致。当脚本中出现中文,且未指定编码类型,那么python2在处理脚本数据时,会用默认的ASCII去解析中文,导致上面的error。

    python2的脚本文件,一般都会说明字符编码类型,如#-*- encoding: utf-8 -*-

    详情: http://python.org/dev/peps/pep-0263/

  2. UnicodeDecodeError: ‘ascii‘ codec can‘t decode byte 0xe5 in position 0: ordinal not in range(128)

    这类问题一般出现在python2中,在编码类型转换中触发。

    python2中有两个编解码函数: decodeencode,用于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‘\u5f20\u4e09‘
    >>> 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无法解析,故报错

  3. 乱码问题。

    乱码不是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中对编码问题的优化

  1. 脚本中的字符不再受系统默认编码的影响,统一为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),不再受系统默认编码影响

  2. 脚本默认编码方式为utf-8, 即不必在脚本开头声明# -*- encoding: utf-8 -*-,也可以在脚本中随意使用中文

  3. 将字符串和字节序列做了区分。增加了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‘>

相关推荐