返回介绍

4.7 Unicode 文本排序

发布于 2024-02-05 21:59:48 字数 4365 浏览 0 评论 0 收藏 0

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

下面对一个生长在巴西的水果的列表进行排序:

>>> fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
>>> sorted(fruits)
['acerola', 'atemoia', 'açaí', 'caju', 'cajá']

不同的区域采用的排序规则有所不同,葡萄牙语等很多语言按照拉丁字母表排序,重音符号和下加符对排序几乎没什么影响。9 因此,排序时“cajá”视作“caja”,必定排在“caju”前面。

9变音符号对排序有影响的情况很少发生,只有两个词之间唯有变音符号不同时才有影响。此时,带有变音符号的词排在常规词的后面。

排序后的 fruits 列表应该是:

['açaí', 'acerola', 'atemoia', 'cajá', 'caju']

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

使用 locale.strxfrm 函数之前,必须先为应用设定合适的区域设置,还要祈祷操作系统支持这项设置。在区域设为 pt_BR 的 GNU/Linux(Ubuntu 14.04)中,可以使用示例 4-19 中的命令。

示例 4-19 使用 locale.strxfrm 函数做排序键

>>> import locale
>>> locale.setlocale(locale.LC_COLLATE, 'pt_BR.UTF-8')
'pt_BR.UTF-8'
>>> fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
>>> sorted_fruits = sorted(fruits, key=locale.strxfrm)
>>> sorted_fruits
['açaí', 'acerola', 'atemoia', 'cajá', 'caju']

因此,使用 locale.strxfrm 函数做排序键之前,要调用 setlocale(LC_COLLATE, «your_locale»)。

不过,有几点要注意。

区域设置是全局的,因此不推荐在库中调用 setlocale 函数。应用或框架应该在进程启动时设定区域设置,而且此后不要再修改。

操作系统必须支持区域设置,否则 setlocale 函数会抛出 locale.Error: unsupported locale setting 异常。

必须知道如何拼写区域名称。它在 Unix 衍生系统中几乎已经形成标准,要通过 'language_code.encoding' 获取。10 但是在 Windows 中,句法复杂一些:Language Name-Language Variant_Region Name.codepage。注意,“Language Name”(语言名称)、“Language Variant”(语言变体)和“Region Name”(区域名)中可以包含空格;除了第一部分之外,其他部分的前面是不同的字符:一个连字符、一个下划线和一个点号。除了语言名称之外,其他部分好像都是可选的。例如,English_United States.850,它的语言名称是“English”,区域是“United States”,代码页是“850”。Windows 能理解的语言名称和区域名见于 MSDN 中的文章“Language Identifier Constants and Strings”,还有“Code Page Identifiers.aspx)”一文列出了最后一部分的代码页数字。11

操作系统的制作者必须正确实现了所设的区域。我在 Ubuntu 14.04 中成功了,但在 OS X(Mavericks 10.9)中却失败了。在两台 Mac 中,调用 setlocale(LC_COLLATE, 'pt_BR.UTF-8') 返回的都是字符串 'pt_BR.UTF-8',没有任何问题。但是,sorted(fruits, key=locale.strxfrm) 得到的结果与 sorted(fruits) 一样,是错误的。我还在 OS X 中尝试了 fr_FR、es_ES 和 de_DE,但是 locale.strxfrm 并未起作用。12

10在 Linux 操作系统中,中国大陆的读者可以使用 zh_CN.UTF-8,简体中文会按照汉语拼音顺序进行排序,它也能对葡萄牙语进行正确排序。——编者注

11感谢 Leonardo Rochael,他所做的工作超出了身为技术审校的职责,虽然他是 GNU/Linux 用户,但却研究了这些 Windows 细节。

12同样,我没找到解决方案,不过却发现其他人也报告了同样的问题。本书技术审校之一 Alex Martelli,在他装有 OS X 10.9 的 Mac 电脑中使用 setlocale 和 locale.strxfrm 时没有遇到问题。综上:结果因人而异。

因此,标准库提供的国际化排序方案可用,但是似乎只支持 GNU/Linux(可能也支持 Windows,但你得是专家)。即便如此,还要依赖区域设置,而这会为部署带来问题。

幸好,有个较为简单的方案:PyPI 中的 PyUCA 库。

使用Unicode排序算法排序

James Tauber,一位高产的 Django 贡献者,他一定是感受到了这一痛点,因此开发了 PyUCA 库,这是 Unicode 排序算法(Unicode Collation Algorithm,UCA)的纯 Python 实现。示例 4-20 展示了它的简单用法。

示例 4-20 使用 pyuca.Collator.sort_key 方法

>>> import pyuca
>>> coll = pyuca.Collator()
>>> fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola']
>>> sorted_fruits = sorted(fruits, key=coll.sort_key)
>>> sorted_fruits
['açaí', 'acerola', 'atemoia', 'cajá', 'caju']

这样做更友好,而且恰好可用。我在 GNU/Linux、OS X 和 Windows 中做过测试。目前,PyUCA 只支持 Python 3.x。13

132015 年 5 月,PyUCA 重新支持 Python 2.x,参见:http://jktauber.com/2015/05/13/pyuca-supports-python-2-again。——编者注

PyUCA 没有考虑区域设置。如果想定制排序方式,可以把自定义的排序表路径传给 Collator() 构造方法。PyUCA 默认使用项目自带的 allkeys.txt,这就是 Unicode 6.3.0 的“Default Unicode Collation Element Table”的副本。

顺便说一下,那个表是 Unicode 数据库中众多表中的一个。下一节会讨论这个话题。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文