Unicode 标准提供了一个完整的数据库(许多格式化的文本文件),不仅包括码位与字符名称之间的映射,还有各个字符的元数据,以及字符之间的关系。例如,Unicode 数据库记录了字符是否可以打印、是不是字母、是不是数字,或者是不是其他数值符号。字符串的 isidentifierisprintableisdecimalisnumeric 等方法就是靠这些信息作判断的。 str.casefold 方法也用到了 Unicode 表中的信息。

unicodedata 模块中有几个函数用于获取字符的元数据。例如,字符在标准中的官方名称是不是组合字符(如结合波形符构成的变音符号等),以及符号对应的人类可读数值(不是码位)。示例 4-21 展示了 unicodedata.name()unicodedata.numeric() 函数,以及字符串的 .isdecimal().isnumeric() 方法的用法。

示例 4-21 Unicode 数据库中数值字符的元数据示例(各个标号说明输出中的各列)

import unicodedata
import re

re_digit = re.compile(r'\d')

sample = '1\xbc\xb2\u0969\u136b\u216b\u2466\u2480\u3285'

for char in sample:
    print('U+%04x' % ord(char),                        ➊
          char.center(6),                              ➋
          're_dig' if re_digit.match(char) else '-',   ➌
          'isdig' if char.isdigit() else '-',          ➍
          'isnum' if char.isnumeric() else '-',        ➎
          format(unicodedata.numeric(char), '5.2f'),   ➏
          unicodedata.name(char),                      ➐
          sep='\t')

U+0000 格式的码位。

➋ 在长度为 6 的字符串中居中显示字符。

➌ 如果字符匹配正则表达式 r'\d',显示 re_dig

➍ 如果 char.isdigit() 返回 True,显示 isdig

➎ 如果 char.isnumeric() 返回 True,显示 isnum

➏ 使用长度为 5、小数点后保留 2 位的浮点数显示数值。

➐ Unicode 标准中字符的名称。

运行示例 4-21 得到的结果如图 4-3 所示。

图 4-3 中的第 6 列是在字符上调用 unicodedata.numeric(char) 函数得到的结果。这表明,Unicode 知道表示数字的符号的数值。因此,如果你想创建一个支持泰米尔数字和罗马数字的电子表格应用,那就尽管去做吧!

{%}

图 4-3:9 个数值字符及其元数据;re_dig 表示字符匹配正则表达式 r'\d'

图 4-3 表明,正则表达式 r'\d' 能匹配数字“1”和梵文数字 3,但是不能匹配 isdigit 方法判断为数字的其他字符。re 模块对 Unicode 的支持并不充分。PyPI 中有个新开发的 regex 模块,它的最终目的是取代 re 模块,以提供更好的 Unicode 支持。14 下一节会回过头来讨论 re 模块。

14不过在这个示例中,它在识别数字方面的表现没有 re 模块好。

本章使用了 unicodedata 模块中的几个函数,但是还有很多没有用到。详情参阅标准库文档对 unicodedata 模块的说明(https://docs.python.org/3/library/unicodedata.html)。

在结束对字符串和字节序列的讨论之前,我们还要简要说明一个新的趋势——双模式 API,即提供的函数能接受字符串或字节序列为参数,然后根据类型进行特殊处理。