zoukankan      html  css  js  c++  java
  • Python编码问题

    python3与python2的区别

    Python2 的默认编码是 asscii,这也是导致 Python2 中经常遇到编码问题的原因之一

    Python 3 默认采用了 UTF-8 作为默认编码,因此不再需要在文件顶部写 # coding=utf-8 了

    # python2.7
    >>> sys.getdefaultencoding()
    'ascii'
    
    # python3.5
    >>> sys.getdefaultencoding()
    'utf-8'
    

    字符串

    版本 字节码 字符串 默认编码
    python2 str unicode ascii
    python3 bytes str utf-8

    py2 想定义一个日常的字符串,需要手动 u"xxx","xxx" 定义的是当前编码设定下的字节码。

    # python2
    >>> a="你好"
    >>> type(a)
    <type 'str'>
    >>> isinstance(a, unicode)
    False
    >>> isinstance(a, bytes)
    True
    >>> a=u"你好"
    >>> type(a)
    <type 'unicode'>
    

    在 py3 中,"xxx" 定义的就是字符串(unicode编码),b"xxx" 定义的才是字节码。

    >>> a = "你好"
    >>> type(a)
    <class 'str'>
    >>> isinstance(a, bytes)
    False
    >>> a = u"你好"
    >>> type(a)
    <class 'str'>
    >>> a = b"你好"
      File "<stdin>", line 1
    SyntaxError: bytes can only contain ASCII literal characters.
    >>> a = b"secret"
    >>> type(a)
    <class 'bytes'>
    

    所以在 py3 中,coding: utf-8 的注释没有任何用处了。在 py2 中它会隐式的根据指定编码对 str 做字节码的转换,但在 py3 中已经没有这种隐式的转换和定义了。

    py2编码问题的坑

    在 py2 中经常遇到的错误:

    if __name__ == "__main__":
        a="你好"
    #  File "test_py2.py", line 2
    # SyntaxError: Non-ASCII character 'xe4' in file test_py2.py on line 2, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
    

    在py2中会隐式地尝试使用默认编码方式(ascii)将 "你好" 编码为字节码,而ascii编码无法对中文编码,因此报错

    # coding=utf-8
    
    if __name__ == "__main__":
        "你好".encode('utf-8')
    
    # Traceback (most recent call last):
    #   File "test_py2.py", line 4, in <module>
    #    "你好".encode('utf-8')
    # UnicodeDecodeError: 'ascii' codec can't decode byte 0xe4 in position 0: ordinal not in range(128)
    

    首先我们已经知道py2中默认字符串为字节码,那么"你好"应该是一个utf-8编码方式(文件定义的编码方式)生成的字节码,怎么能对字节码再进行编码呢?

    因为 py 以 unicode 作为运算基准,所以在执行编码操作前会先隐性地将字节码 decode 至 unicode,并使用默认字符集(并非文件定义的编码方式)ascii做解码,因此就无法解码utf-8编码形成的字节码

    而在 py3 中是不允许这种操作的。从 bytes 到另一 bytes 你必须先 decode 后才能 encode。

    Unicode与字节码

    Unicode是什么

    Unicode的发明目的是让世界上的每一个符号都有一个独一无二的编码

    Unicode 是一个符号集,它规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。

    为什么不直接存储Unicode编码?

    符号的Unicode编码有的长有的短。

    比如 'a' 的unicode编码是7位,一个字节就能存下(与ascii编码相同)

    >>> ord("a")
    97 // Unicode编码的十进制表示法
    >>> hex(ord("a"))
    '0x61'  // 转成16进制就是我们通看到的Unicode编码格式
    >>> bin(ord("a"))
    '0b1100001' // 转成二进制,可以看到 "a" 的Unicode编码有7位,一个字节
    

    而汉字的unicode编码通常需要两个字节才能存下

    >>> ord("严")
    20005
    >>> hex(ord("严"))
    '0x4e25'
    >>> bin(ord("严"))
    '0b100111000100101' // “严”的unicode编码有15位,需要两个字节才能存下
    

    计算机并不知道几个字节表示一个符号,这就给存储和读取带来了不便。如果统一用两个字节存储一个符号,那么对于英文字符来说,前一个字节就全部是0了,这样是对存储空间的浪费。

    因此,需要一种聪明的方法来存储Unicode编码,既不要浪费存储空间,也要让机器能够分辨几个字节表示一个符号

    UTF-8编码

    UTF-8编码是Unicode的一种实现方式,它将Unicod编码分为四个范围,用1-4个字节表示一个字符,即:

    1)对于单字节的符号,字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。

    2)对于n字节的符号(n > 1),第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。

    Unicode符号范围 (十六进制) UTF-8编码方式(二进制)
    0000 0000-0000 007F 0xxxxxxx
    0000 0080-0000 07FF 110xxxxx 10xxxxxx
    0000 0800-0000 FFFF 1110xxxx 10xxxxxx 10xxxxxx
    0001 0000-0010 FFFF 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

    跟据上表,计算机解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。

    下面,还是以汉字严为例,演示如何实现 UTF-8 编码。

    严的 Unicode 是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此严的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,严的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5。

    >>> "严".encode("utf-8")
    b'xe4xb8xa5'
    >>> for b in "严".encode("utf-8"):
    ...     print(type(b), b, bin(b))
    ... 
    # 字符串的元素是字符,bytes对象的元素则是字节
    # 字节对象本质上是一个 0 <= x < 256 区间内的整数不可变序列
    # 我们可以用方括号来取得每个字节
    # 然后,用bin(b)来直观地看每个字节在内存中的存储方式
    <class 'int'> 228 0b11100100
    <class 'int'> 184 0b10111000
    <class 'int'> 165 0b10100101
    

    参考资料

    python2与python3字符串的区别

    字符编码笔记:ASCII,Unicode 和 UTF-8

    Python3的字节类型(bytes)

    字体编辑用中日韩汉字Unicode编码表

    ASCII码对照表

  • 相关阅读:
    html5 自定义属性data-*
    企业微信接口授权
    js对象---字符串
    谈谈html5新增的元素及其他功能
    模拟缓存
    jdbc数据库连接
    面向对象的理解
    最简单的Spring+SpringMVC+Mybatis的整合
    EF报错 附加类型model失败
    c# Web服务远程“调用”调试
  • 原文地址:https://www.cnblogs.com/luozx207/p/13129770.html
Copyright © 2011-2022 走看看