Unicode数据库

Unicode文本排序

Python比较任何类型的序列时,会一一比较序列里的各个元素。对字符串来说,比较的是码位,可是在比较非ASCII字符时,得到的结果不如人意。

fruits = ['caju', 'atemoia', 'caja', 'acai', 'acerola']
sorted(fruits)

['acai', 'acerola', 'atemoia', 'caja', 'caju']

在Python中,非ASCII文本的标准排序方式是使用locale.strxfrm函数,根据locale模块的文档,这个函数会把字符串转换成适合所在区域进行比较的形式

使用locale.strxfrm函数之前,必须为应用设定合适的区域设置,还要希望操作系统支持这项设置。

在使用locale.strxfrm函数做排序键之前,要调用setlocale(LC_COLLATE,your_locale)

  • 区域设置时全局的,因此不推荐在库中调用setlocale函数。应用或框架应该在进程启动时设定区域设置,而且此后不要再修改
  • 操作系统必须支持区域设定,否则setlocale函数会抛出locale.Error: unsupported locale setting异常
  • 必须知道如何拼写区域名称。它在Unix衍生系统中几乎已经形成标准,要通过‘language_code.encoding'获取。但是在Windows中,句法复杂一些:
  • 操作系统的制作者必须正确实现了所设区域
  • 标准库提供的国际化排序方式可用,但是似乎只支持GNU/Linux。即使如此,还要依赖区域设置。

使用Unicode排序算法排序

通过PyUCA(Unicode Collation Algorithm,UCA)库,使用pyuca.Collator.sort_key方法

import pyuca

coll = pyuca.Collator()
fruits = ['caju', 'atemoia', 'caja', 'acai', 'acerola']
sorted_fruits = sorted(fruits, key=coll.sort_key)
sorted_fruits

['acai', 'acerola', 'atemoia', 'caja', 'caju']

PyUCA没有考虑区域设置,如果要定制排序方式,可以把自定义的排序表路径传给Collator()构造方法。PyUCA默认使用项目自带的allkey.txt

Unicode数据库

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

unicodedata模块中有几个函数用于获取字符的元数据。例如,字符在标准中的官方名称是不是组合字符(如结合波形符构成的变音符号等),以及符号对应的人类可读数值

# 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), # 输出U+0000格式的码位
          char.center(6), # 在长度为6的字符串居中显示字符
          're_dig' if re_digit.match(char) else '-' # 如果字符匹配正则表达式r'\d',显示re_dig,
          'isdig' if char.isdigit() else '-', # 如果char.isdigit()返回True,显示isdig
          'isnum' if char.isnumeric() else '-', # 如果char.isnumeric()返回True,显示isnum
          format(unicodedata.numeric(char), '5.2f'), # 使用长度为5、小数点后保留两位的浮点数来显示数值
          unicodedata.name(char), # Unicode标准中字符的名称
          sep='\t') # 间隔为'\t'

U+0031 1 re_dig isnum 1.00 DIGIT ONE
U+00bc ¼ - isnum 0.25 VULGAR FRACTION ONE QUARTER
U+00b2 ² -isdig isnum 2.00 SUPERSCRIPT TWO
U+0969 ३ re_dig isnum 3.00 DEVANAGARI DIGIT THREE
U+136b ፫ -isdig isnum 3.00 ETHIOPIC DIGIT THREE
U+216b Ⅻ - isnum 12.00 ROMAN NUMERAL TWELVE
U+2466 ⑦ -isdig isnum 7.00 CIRCLED DIGIT SEVEN
U+2480 ⒀ - isnum 13.00 PARENTHESIZED NUMBER THIRTEEN
U+3285 ㊅ - isnum 6.00 CIRCLED IDEOGRAPH SIX

正则表达式r'\d'能匹配数字'1'和梵文数字'3',但是不能匹配isdigit方法判断为数字的其他字符。re模块对Unicode的支持并不充分。PyPI中有regex模块,它的最终目的是取代re模块,以提供更好的Unicode支持

posted @ 2021-12-16 10:52  里列昂遗失的记事本  阅读(184)  评论(0)    收藏  举报