python中提供了标准的排序方法但是在内置的方法可能带来一个不好的结果,尤其是比较非 ASCII 字符时。例如下面的例子.
由于不同的区域才去的排序规则不一样,下面的例子实际的排序是这样子的:
['açaí', 'acerola', 'atemoia', 'cajá', 'caju']
而现在的排序结果确实这样子的:
['acerola', 'atemoia', 'açaí', 'caju', 'cajá']
这是由于葡萄牙语等很多语言按照拉丁字母表排序,重音符号和下加符对排序几乎没什么影响.
在python中,非 ASCII 文本的标准排序方式是使用 locale.strxfrm 函数,把字符串转换成适合所在区域进行比较的形式,使用 locale.strxfrm 函数之前,必须先为应用设定合适的区域设置,这里还有一个条件是操作系统支持这项设置.
fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
print(sorted(fruits))
['acerola', 'atemoia', 'açaí', 'caju', 'cajá']
使用PyUCA 库来解决上面的烦恼,Unicode 排序算法(Unicode Collation
Algorithm,UCA)的纯 Python 实现。目前支持 Python 3.x版本.这里需要自己去下载,在windows下载路径.
PyUCA 没有考虑区域设置,如果想定制排序方式,可以把自定义的排序表路径传给
Collator() 构 造 方 法。
import pyuca
coll = pyuca.Collator()
fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
sorted_fruits = sorted(fruits,key=coll.sort_key)
print(sorted_fruits)
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、小数点后保留 2 位的浮点数显示数值
unicodedata.name(char), # Unicode 标准中字符的名称
sep ='\t'
)
U+0031 1 re_dig isdig 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 isdig 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
标准库中的一些函数能接受字符串或字节序列为参数,然后根据类型展现不同的行为
如果使用字节序列构建正则表达式,\d 和 \w 等模式只能匹配 ASCII 字符;相比之下,如果是字符串模式,就能匹配 ASCII 之外的 Unicode 数字或字。
可以使用正则表达式搜索字符串和字节序列,但是在后一种情况中,ASCII 范围外的字节不会当成数字和组成单词的字母母。
import re
# 前两个正则表达式是字符串类型
re_numbers_str = re.compile(r'\d+')
re_words_str = re.compile(r'\w+')
# 后两个正则表达式是字节序列类型
re_numbers_bytes = re.compile(rb'\d+')
re_words_bytes = re.compile(rb'\w+')
# 要搜索的 Unicode 文本,包括 1729 的泰米尔数字
text_str = ("Ramanujan saw \u0be7\u0bed\u0be8\u0bef"
" as 1729 = 1³ + 12³ = 9³ + 10³.")
# 字节序列只能用字节序列正则表达式搜索
text_bytes = text_str.encode('utf_8')
print('Text', repr(text_str), sep='\n ')
print('Numbers')
# 字符串模式 r'\d+' 能匹配泰米尔数字和 ASCII 数字
print(' str :', re_numbers_str.findall(text_str))
#字节序列模式 rb'\d+' 只能匹配 ASCII 字节中的数字
print(' bytes:', re_numbers_bytes.findall(text_bytes))
print('Words')
# 字符串模式 r'\w+' 能匹配字母、上标、泰米尔数字和 ASCII 数字
print(' str :', re_words_str.findall(text_str))
# 字节序列模式 rb'\w+' 只能匹配 ASCII 字节中的字母和数字
print(' bytes:', re_words_bytes.findall(text_bytes))
Text
'Ramanujan saw ௧௭௨௯ as 1729 = 1³ + 12³ = 9³ + 10³.'
Numbers
str : ['௧௭௨௯', '1729', '1', '12', '9', '10']
bytes: [b'1729', b'1', b'12', b'9', b'10']
Words
str : ['Ramanujan', 'saw', '௧௭௨௯', 'as', '1729', '1³', '12³', '9³', '10³']
bytes: [b'Ramanujan', b'saw', b'as', b'1729', b'1', b'12', b'9', b'10']
os 模 块 中 的 所 有 函 数、 文 件 名 或 路 径 名 参 数 既 能 使 用 字 符串,也能使用字节序列。如果这样的函数使用字符串参数调用,该参数会使用 sys.getfilesystemencoding() 得到的编解码器自动编码,然后操作系统会使用相同的编解码器解码。
如果 filename 是 str 类型(此外还可能是 bytes 类型),使用 sys.getfilesystemencoding()
返回的编解码器把 filename 编码成字节序列;否则,返回未经修改的 filename 字节
序列。
如 果 filename 是 bytes 类 型( 此 外 还 可 能 是 str 类 型 ), 使 用 sys.getfilesystemen-
coding() 返回的编解码器把 filename 解码成字符串;否则,返回未经修改的 filename
字符串。
分享关于人工智能,机器学习,深度学习以及计算机视觉的好文章,同时自己对于这个领域学习心得笔记。想要一起深入学习人工智能的小伙伴一起结伴学习吧!扫码上车!