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支持

上一篇:CentOS 设置中文环境


下一篇:locale 语法的使用