文本和字节序列

Posted xiangxiaolin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文本和字节序列相关的知识,希望对你有一定的参考价值。

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
bcafxc3xa9 # ?
>>> 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
bcafxc3xa9
>>> cafe[0] ?
99
>>> cafe[:1] ?
bc
>>> cafe_arr = bytearray(cafe)
>>> cafe_arr ?
bytearray(bcafxc3xa9)
>>> cafe_arr[-1:] ?
bytearray(bxa9)
? 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)
b1Kxcexa9
构建 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) # ?
bGIF89a+x02xe6x00
>>> struct.unpack(fmt, header) # ?
(bGIF, b89a, 555, 230)
>>> del header # ?
>>> del img
? 结构体的格式:< 是小字节序,3s3s 是两个 3 字节序列,HH 是两个16 位二进制整数。
? 使用内存中的文件内容创建一个 memoryview 对象……
? ……然后使用它的切片再创建一个 memoryview 对象;这里不会复制字节序列。
? 转换成字节序列,这只是为了显示;这里复制了 10 字节。
? 拆包 memoryview 对象,得到一个元组,包含类型、版本、宽度和高度。
? 删除引用,释放 memoryview 实例所占的内存。
 
 

以上是关于文本和字节序列的主要内容,如果未能解决你的问题,请参考以下文章

文本和字节序列

文本和字节序列

干货通俗理解自然语言处理中N-Gram模型

C语言如何实现拷贝图片?几行代码即可搞定

字符串与bytes

序列化与字节码转换