- 前言
- 目标读者
- 非目标读者
- 本书的结构
- 以实践为基础
- 硬件
- 杂谈:个人的一点看法
- Python 术语表
- Python 版本表
- 排版约定
- 使用代码示例
- 第一部分 序幕
- 第 1 章 Python 数据模型
- 第二部分 数据结构
- 第 2 章 序列构成的数组
- 第 3 章 字典和集合
- 第 4 章 文本和字节序列
- 第三部分 把函数视作对象
- 第 5 章 一等函数
- 第 6 章 使用一等函数实现设计模式
- 第 7 章 函数装饰器和闭包
- 第四部分 面向对象惯用法
- 第 8 章 对象引用、可变性和垃圾回收
- 第 9 章 符合 Python 风格的对象
- 第 10 章 序列的修改、散列和切片
- 第 11 章 接口:从协议到抽象基类
- 第 12 章 继承的优缺点
- 第 13 章 正确重载运算符
- 第五部分 控制流程
- 第 14 章 可迭代的对象、迭代器和生成器
- 14.1 Sentence 类第1版:单词序列
- 14.2 可迭代的对象与迭代器的对比
- 14.3 Sentence 类第2版:典型的迭代器
- 14.4 Sentence 类第3版:生成器函数
- 14.5 Sentence 类第4版:惰性实现
- 14.6 Sentence 类第5版:生成器表达式
- 14.7 何时使用生成器表达式
- 14.8 另一个示例:等差数列生成器
- 14.9 标准库中的生成器函数
- 14.10 Python 3.3 中新出现的句法:yield from
- 14.11 可迭代的归约函数
- 14.12 深入分析 iter 函数
- 14.13 案例分析:在数据库转换工具中使用生成器
- 14.14 把生成器当成协程
- 14.15 本章小结
- 14.16 延伸阅读
- 第 15 章 上下文管理器和 else 块
- 第 16 章 协程
- 第 17 章 使用期物处理并发
- 第 18 章 使用 asyncio 包处理并发
- 第六部分 元编程
- 第 19 章 动态属性和特性
- 第 20 章 属性描述符
- 第 21 章 类元编程
- 结语
- 延伸阅读
- 附录 A 辅助脚本
- Python 术语表
- 作者简介
- 关于封面
4.7 Unicode 文本排序
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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论