Humans use text. Computers speak bytes.
----Esther Nam and Travis Fischer, Character Encoding and Unicode in Python
Python3明确区分了人类可读的文本字符串和原始序列字符串。隐式地把字节序列转换成Unicode文本已成过去。本章将讨论Unicode字符串、二进制序列以及二者之间转换时使用的编码。
根据您的 Python 编程场景,深入理解Unicode对您来说可能重要也可能不重要。最后,本章涉及的大多数问题对只处理 ASCII 文本的程序员没有影响。但是即便如此,也不能避而不谈字符串和字节序列的区别。此外,你会发现专门的二进制序列类型所提供的功能,是Python2中全功能的str类型所不具有的。
本章将讨论以下话题:
- 字符、码位和字节表述
- 二进制序列的独特功能:
bytes
,bytearray
,和memoryview
- 完整 Unicode 和旧字符集的编解码器
- 避免和处理编码错误
- 处理文本文件的最佳实践
- 默认编码的陷阱和标准I/O问题
- 标准化的进行安全的 Unicode 文本比较
- 用于标准化、大小写折叠和强力移除音调符号的实用函数
- 使用locale模块和PyUCA库正确的排序Uncode文本
- Unicode 数据库中的字符元数据
- 能够处理str和bytes的双模式API
- 从字符组合构建表情符号
本章新增的内容
Python 3 中对 Unicode 的支持已经全面稳定了一段时间,所以本章最大的变化是新的表情符号部分——不是因为 Python 的变化,而是因为表情符号和表情符号组合的日益流行。2020 年发布的 Unicode 13 支持 3000 多个表情符号,其中许多是通过组合 Unicode 字符构建的,详见“Multi-character emojis”
第二版中的另一个新内容是“按名称查找字符”,包括用于搜索 Unicode 数据库的源代码,这是从命令行查找带圆圈数字和微笑的猫的好方法。
值得一提的一个小变化是 Windows 上的 Unicode 支持,它比 Python 3.6 更好、更简单,我们将在“注意编码默认值”中看到。
字符问题
“字符串”的概念很简单:一个字符串就是一个字符序列。问题在于”字符“的定义。
2021 年,我们对“字符”的最佳定义是 Unicode 字符。因此,您从 Python 3 str 中获得的元素是 Unicode 字符,就像 Python 2 中的 unicode 对象的元素一样——而不是您从 Python 2 str 中获得的原始字节。
Unicode 标准明确地将字符的标识与特定的字节表述进行区分:
字符的标识——即code point(码位)——是一个从 0 到 1,114,111(十进制)的数字,在 Unicode 标准中显示为 4 到 6 个带有“U+”前缀的十六进制数字,从 U+0000 到 U+10FFFF。例如,字母 A 的码位是 U+0041,欧元符号是 U+20AC,音乐符号 G 谱号码位为 U+1D11E。大约 13% 的有效码位在 Unicode 13.0.0(Python 3.9 中使用的标准)中分配了字符。- 代表字符的实际字节取决于所用的编码。编码是一种将码位转换为字节序列的算法,解码是将字节序列转换为码位的算法。字母 A (U+0041) 的码位在 UTF-8 编码中编码为单个字节 \x41,而在 UTF-16LE 编码中编码为两个字节 \x41\x00。再举一个例子,UTF-8 需要三个字节——\xe2\x82\xac——来编码欧元符号 (U+20AC),但在 UTF-16LE 中,相同的码位被编码为两个字节:\xac\x20。
从码位转换为字节是编码;从字节到码位的转换就是解码。请参见下面示例。
>>> s = 'café'
>>> len(s)
4
>>> b = s.encode('utf8')
>>> b
b'caf\xc3\xa9'
>>> len(b)
5
>>> b.decode('utf8')
'café'
字节概要
新的二进制序列类型在很多方面和Python2的str类型不同。首先要知道的是,二进制序列有两种基本的内置类型:Python 3 中引入的不可变的bytes类型和 Python 2.6 中添加的可变的bytearray类型。
bytes 或 bytearray对象中的每一个元素都是 0 到 255 之间的整数,而不是像 Python 2 str对象那样是单个的字符。然而,二进制序列的切片总是产生相同类型的二进制序列——包括长度为 1 的切片,如下例所示:
>>> cafe = bytes('café', encoding='utf_8')
>>> cafe
b'caf\xc3\xa9'
>>> cafe[0]
99
>>> cafe[:1]
b'c'
>>> cafe_arr = bytearray(cafe)
>>> cafe_arr
bytearray(b'caf\xc3\xa9')
>>> cafe_arr[-1:]
bytearray(b'\xa9')
注意:my_bytes[0] 获取了一个 int 但 my_bytes[:1] 返回一个长度为 1 的 bytes 对象的事实可能令人惊讶,并使得处理二进制数据的程序难以同时支持 Python 2.7 和 Python 3。但它与许多其他语言以及其他 Python 序列类型一致——除了 str这个序列类型,它是唯一一个 s[0] == s[:1] 的序列类型。
虽然二进制序列实际上是整数序列,但它们的字面量表示表明其中有ASCII 文本。因此,每个字节的值有以下四种方式进行展示:
- 对于可打印 ASCII 范围内的字节(从空格到 ~),使用 ASCII 字符本身。
- 对于与制表符、换行符、回车符和 \ 对应的字节,使用转义序列 \t、\n、\r 和 \\。
- 如果字符串分隔符 ' 和 " 都出现在字节序列中,则整个序列由 ' 分隔,其中的任何 ' 都转义为 \'。
- 对于其他字节值,使用十六进制转义序列(例如,\x00 是空字节)。
这就是为什么我们在上例看到的是b'caf\xc3\xa9',前三个字节b‘caf’在可打印的ASCII范围内,而后两个字节则不然。
除了格式化(format、format_map)和其他一些依赖 Unicode 数据之外的方法,包括 casefold、isdecimal、isidentifier、isnumeric、isprintable 和 encode,bytes和bytearray类型支持str类型的其他所有方法。这意味着您可以使用熟悉的字符串方法,如 endwith、replace、strip、translate、upper 和许多其他二进制序列——只使用bytes而不是 str 参数。此外,如果正则表达式是从二进制序列而不是 str 编译的,re 模块中的正则表达式函数也能处理于二进制序列。从 Python 3.5 开始,% 运算符可以再次处理二进制序列。
二进制序列有一个 str 没有的类方法,名为 fromhex,它通过解析可选用空格分隔的十六进制数字对来构建二进制序列:
>>> bytes.fromhex('31 4B CE A9')
b'1K\xce\xa9'
构建bytes或bytearray实例的其他方法是调用它们的构造函数:
- 一个 str对象 和encoding关键字参数
- 一个可迭代对象,对象中的每个元素为0-255之间的数值
- 一个实现缓存协议的对象(例如.,
bytes
,bytearray
,memoryvi