• 文本和字节序列


    1 字符问题
    “字符串”是个相当简单的概念:一个字符串是一个字符序列。问题出在“字符”的定义上。
    在 2015 年,“字符”的最佳定义是 Unicode 字符。因此,从 Python 3 的str 对象中获取的元素是 Unicode 字符,这相当于从 Python 2 的unicode 对象中获取的元素,而不是从 Python 2 的 str 对象中获取的原始字节序列。
    Unicode 标准把字符的标识和具体的字节表述进行了如下的明确区分。
      
    字符的标识,即码位,是 0~1 114 111 的数字(十进制),在Unicode 标准中以 4~6 个十六进制数字表示,而且加前缀“U+”。例如,字母 A 的码位是 U+0041,欧元符号的码位是 U+20AC,高音谱号的码位是 U+1D11E。在 Unicode 6.3 中(这是 Python 3.4 使用的标准),约 10% 的有效码位有对应的字符。
    字符的具体表述取决于所用的编码。编码是在码位和字节序列之间转换时使用的算法。在 UTF-8 编码中,A(U+0041)的码位编码成单个字节 x41,而在 UTF-16LE 编码中编码成两个字节x41x00。再举个例子,欧元符号(U+20AC)在 UTF-8 编码中是三个字节——xe2x82xac,而在 UTF-16LE 中编码成两个字节:xacx20。
    把码位转换成字节序列的过程是编码;把字节序列转换成码位的过程是解码。示例 4-1 阐释了这一区分。
    >>> s = 'café'
    >>> len(s) #
    4
    >>> b = s.encode('utf8') #
    >>> b
    b'cafxc3xa9' #
    >>> len(b) #
    5
    >>> b.decode('utf8') #
    'café
    ❶ 'café' 字符串有 4 个 Unicode 字符。
    ❷ 使用 UTF-8 把 str 对象编码成 bytes 对象。
    ❸ bytes 字面量以 b 开头。
    ❹ 字节序列 b 有 5 个字节(在 UTF-8 中,“é”的码位编码成两个字节)。
    ❺ 使用 UTF-8 把 bytes 对象解码成 str 对象。
    如果想帮助自己记住 .decode() 和 .encode() 的区别,可以把字节序列想成晦涩难懂的机器磁芯转储,把 Unicode 字符串想成“人类可读”的文本。那么,把字节序列变成人类可读的文本字符串就是解码,而把字符串变成用于存储或传输的字节序列就是编码。
    虽然 Python 3 的 str 类型基本相当于 Python 2 的 unicode 类型,只不过是换了个新名称,但是 Python 3 的 bytes 类型却不是把 str 类型换个名称那么简单,而且还有关系紧密的 bytearray 类型。因此,在讨论编码和解码的问题之前,有必要先来介绍一下二进制序列类型。
     
    2 字节概要
    新的二进制序列类型在很多方面与 Python 2 的 str 类型不同。首先要知道,Python 内置了两种基本的二进制序列类型:Python 3 引入的不可变bytes 类型和 Python 2.6 添加的可变 bytearray 类型。(Python 2.6 也引入了 bytes 类型,但那只不过是 str 类型的别名,与 Python 3 的bytes 类型不同。)
    >>> cafe = bytes('café', encoding='utf_8') ➊
    >>> cafe
    b'cafxc3xa9'
    >>> cafe[0] ➋
    99
    >>> cafe[:1] ➌
    b'c'
    >>> cafe_arr = bytearray(cafe)
    >>> cafe_arr ➍
    bytearray(b'cafxc3xa9')
    >>> cafe_arr[-1:] ➎
    bytearray(b'xa9')
    ❶ bytes 对象可以从 str 对象使用给定的编码构建。
    ❷ 各个元素是 range(256) 内的整数。
    ❸ bytes 对象的切片还是 bytes 对象,即使是只有一个字节的切片。
    ❹ bytearray 对象没有字面量句法,而是以 bytearray() 和字节序列字面量参数的形式显示。
    ❺ bytearray 对象的切片还是 bytearray 对象。
    my_bytes[0] 获取的是一个整数,而 my_bytes[:1] 返回的是一个长度为 1 的 bytes 对象——这一点应该不会让人意外。s[0] == s[:1] 只对 str 这个序列类型成立。不过,str 类型的这个行为十分罕见。对其他各个序列类型来说,s[i] 返回一个元素,而 s[i:i+1] 返回一个相同类型的序列,里面是 s[i] 元素。

    二进制序列有个类方法是 str 没有的,名为 fromhex,它的作用是解析十六进制数字对(数字对之间的空格是可选的),构建二进制序列:
    >>> bytes.fromhex('31 4B CE A9')
    b'1Kxcexa9'
    构建 bytes 或 bytearray 实例还可以调用各自的构造方法,传入下述参数。
    一个 str 对象和一个 encoding 关键字参数。
    一个可迭代对象,提供 0~255 之间的数值。
    一个整数,使用空字节创建对应长度的二进制序列。[Python 3.5 会把这个构造方法标记为“过时的”,Python 3.6 会将其删除。参见“PEP 467—Minor API improvements for binarysequences”(https://www.python.org/dev/peps/pep-0467/)。]
    一个实现了缓冲协议的对象(如bytes、bytearray、memoryview、array.array);此时,把源对象中的字节序列复制到新建的二进制序列中。
     
    结构体和内存视图
    struct 模块提供了一些函数,把打包的字节序列转换成不同类型字段组成的元组,还有一些函数用于执行反向转换,把元组转换成打包的字节序列。struct 模块能处理 bytes、bytearray 和 memoryview 对象。
    memoryview 类不是用于创建或存储字节序列的,而是共享内存,让你访问其他二进制序列、打包的数组和缓冲中的数据切片,而无需复制字节序列,例如 Python Imaging Library(PIL) 就是这样处理图像的。
      示例 4-4 展示了如何使用 memoryview 和 struct 提取一个 GIF 图像的宽度和高度。
    >>> import struct
    >>> fmt = '<3s3sHH' #
    >>> with open('filter.gif', 'rb') as fp:
    ... img = memoryview(fp.read()) #
    ...
    >>> header = img[:10] #
    >>> bytes(header) #
    b'GIF89a+x02xe6x00'
    >>> struct.unpack(fmt, header) #
    (b'GIF', b'89a', 555, 230)
    >>> del header #
    >>> del img
    ❶ 结构体的格式:< 是小字节序,3s3s 是两个 3 字节序列,HH 是两个16 位二进制整数。
    ❷ 使用内存中的文件内容创建一个 memoryview 对象……
    ❸ ……然后使用它的切片再创建一个 memoryview 对象;这里不会复制字节序列。
    ❹ 转换成字节序列,这只是为了显示;这里复制了 10 字节。
    ❺ 拆包 memoryview 对象,得到一个元组,包含类型、版本、宽度和高度。
    ❻ 删除引用,释放 memoryview 实例所占的内存。
     
     
    人生就是要不断折腾
  • 相关阅读:
    android gradle 多渠道打包
    Gradle Plugin User Guide 中文版
    最好的JAVA IDE IntelliJ IDEA使用简介(一)—之界面元素
    ANDROID 中UID与PID的作用与区别
    HTTP Header 详解
    HTTP协议详解
    Volatile总结
    Condition-线程通信更高效的方式
    JAVA基础知识点(转载的)
    CountDownLatch和CyclicBarrier的区别
  • 原文地址:https://www.cnblogs.com/xiangxiaolin/p/11599518.html
Copyright © 2020-2023  润新知