如何在 Python 中按字母顺序对 unicode 字符串进行排序?
- 2025-02-13 08:36:00
- admin 原创
- 67
问题描述:
Python 默认按字节值排序,这意味着 é 排在 z 后面,还有其他同样有趣的东西。在 Python 中按字母顺序排序的最佳方法是什么?
有这样的库吗?我什么也没找到。排序最好有语言支持,这样它就能理解瑞典语中åäö 应该排在 z 之后,但 ü 应该排在 u 之后,等等。因此 Unicode 支持几乎是必需的。
如果没有相应的库,那么最好的方法是什么?只需将字母映射到整数值,然后将字符串映射到整数列表即可?
解决方案 1:
IBM 的ICU库可以实现这一点(以及更多)。它具有 Python 绑定:PyICU。
更新:ICU 和 之间排序的核心区别locale.strcoll
在于 ICU 使用完整的Unicode 排序算法,而strcoll
使用ISO 14651。
这两种算法之间的差异简要总结如下:http://unicode.org/faq/collation.html#13。这些都是相当奇特的特殊情况,在实践中很少会有影响。
>>> import icu # pip install PyICU
>>> sorted(['a','b','c','ä'])
['a', 'b', 'c', 'ä']
>>> collator = icu.Collator.createInstance(icu.Locale('de_DE.UTF-8'))
>>> sorted(['a','b','c','ä'], key=collator.getSortKey)
['a', 'ä', 'b', 'c']
解决方案 2:
我在答案中没有看到这一点。我的应用程序使用 python 的标准库根据语言环境进行排序。这很容易。
# python2.5 code below
# corpus is our unicode() strings collection as a list
corpus = [u"Art", u"Älg", u"Ved", u"Wasa"]
import locale
# this reads the environment and inits the right locale
locale.setlocale(locale.LC_ALL, "")
# alternatively, (but it's bad to hardcode)
# locale.setlocale(locale.LC_ALL, "sv_SE.UTF-8")
corpus.sort(cmp=locale.strcoll)
# in python2.x, locale.strxfrm is broken and does not work for unicode strings
# in python3.x however:
# corpus.sort(key=locale.strxfrm)
向 Lennart 和其他回答者提出的问题:没有人知道“locale”吗?或者它不能胜任这项任务?
解决方案 3:
您可能也对pyuca感兴趣:
http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/
虽然这肯定不是最精确的方法,但这是一种非常简单的方法,至少可以在一定程度上解决问题。它还胜过 web 应用程序中的 locale,因为 locale 不是线程安全的,并且会在整个进程范围内设置语言设置。它也比依赖外部 C 库的 PyICU 更容易设置。
我将脚本上传到了 github,因为在撰写本文时原始脚本已经关闭,因此我不得不借助网络缓存来获取它:
https://github.com/href/Python-Unicode-Collation-Algorithm
我成功地使用该脚本在 plone 模块中对德语/法语/意大利语文本进行了合理的排序。
解决方案 4:
尝试 James Tauber 的Python Unicode 排序算法。它可能不会完全满足您的要求,但值得一看。有关这些问题的更多信息,请参阅Christopher Lenz 的这篇文章。
解决方案 5:
摘要和扩展答案:
locale.strcoll
在 Python 2 下,locale.strxfrm
实际上可以解决问题,并且效果很好,前提是您已安装相关区域设置。我也在 Windows 下测试过,其中区域设置名称令人困惑地不同,但另一方面,它似乎默认安装了所有受支持的语言环境。
ICU
在实践中并不一定能做得更好,但它能做的更多。最值得注意的是,它支持将不同语言的文本拆分成单词的拆分器。这对于没有单词分隔符的语言非常有用。不过,您需要有一个词库作为拆分的基础,因为这不包括在内。
它还为语言环境提供了很长的名称,这样您就可以获得漂亮的语言环境显示名称,支持公历以外的其他日历(尽管我不确定 Python 接口是否支持这一点)以及大量其他或多或少晦涩难懂的语言环境支持。
总而言之:如果您想按字母顺序和依赖于语言环境进行排序,您可以使用该locale
模块,除非您有特殊要求,或者还需要更多依赖于语言环境的功能,如单词分割器。
解决方案 6:
我看到答案已经做得很好了,只是想指出Human Sort中的一个编码效率低下的问题。要对unicode字符串s应用选择性的逐字符转换,它使用以下代码:
spec_dict = {'Å':'A', 'Ä':'A'}
def spec_order(s):
return ''.join([spec_dict.get(ch, ch) for ch in s])
Python 有一种更好、更快、更简洁的方法来执行这个辅助任务(对于 Unicode 字符串——对于字节字符串的类似方法有一个不同且不太有用的规范!-):
spec_dict = dict((ord(k), spec_dict[k]) for k in spec_dict)
def spec_order(s):
return s.translate(spec_dict)
传递给该方法的字典translate
以 Unicode 序数(而非字符串)作为键,这就是为什么我们需要从原始 char-to-char 重建步骤的原因spec_dict
。(传递给翻译的字典中的值 [与键相反,键必须是序数] 可以是 Unicode 序数、任意 Unicode 字符串或 None 以在翻译过程中删除相应的字符,因此很容易指定“为了排序而忽略某个字符”、“为了排序而将 ä 映射到 ae”等)。
在 Python 3 中,您可以更简单地执行“重建”步骤,例如:
spec_dict = ''.maketrans(spec_dict)
请参阅文档maketrans
,了解在 Python 3 中使用此静态方法的其他方式。
解决方案 7:
完整的 UCA 解决方案
最简单、最轻松、最直接的方法是调用 Perl 库模块Unicode::Collate::Locale,它是标准Unicode::Collate模块的子类。您需要做的就是向构造函数传递"xv"
瑞典的语言环境值。
(对于瑞典语文本,您可能不一定会喜欢这一点,但是由于 Perl 使用抽象字符,因此您可以使用任何您喜欢的 Unicode 代码点 — 无论平台或构建如何!很少有语言能提供这样的便利。我提到这一点是因为我最近一直在与 Java 就这个令人抓狂的问题进行苦战,但都以失败告终。)
问题是我不知道如何从 Python 访问 Perl 模块 — 除了使用 shell 调用或双向管道。为此,我为您提供了一个完整的工作脚本,名为ucsort,您可以调用它来非常轻松地完成您的要求。
此脚本 100% 符合完整的Unicode 排序算法,并支持所有定制选项!!如果您安装了可选模块或运行 Perl 5.13 或更高版本,那么您就可以完全访问易于使用的 CLDR 语言环境。见下文。
示范
想象一个按以下方式排序的输入集:
b o i j n l m å y e v s k h d f g t ö r x p z a ä c u q
默认按代码点排序的结果为:
a b c d e f g h i j k l m n o p q r s t u v x y z ä å ö
这与每个人的观点都不符。使用我的脚本(使用 Unicode 排序算法),您将获得以下顺序:
% perl ucsort /tmp/swedish_alphabet | fmt
a å ä b c d e f g h i j k l m n o ö p q r s t u v x y z
这是默认的 UCA 排序。要获取瑞典语言环境,请按如下方式调用ucsort:
% perl ucsort --locale=sv /tmp/swedish_alphabet | fmt
a b c d e f g h i j k l m n o p q r s t u v x y z å ä ö
下面是一个更好的输入演示。首先,输入集:
% fmt /tmp/swedish_set
cTD cDD Cöd Cbd cAD cCD cYD Cud cZD Cod cBD Cnd cQD cFD Ced Cfd cOD
cLD cXD Cid Cpd cID Cgd cVD cMD cÅD cGD Cqd Cäd cJD Cdd Ckd cÖD cÄD
Ctd Czd Cxd cHD cND cKD Cvd Chd Cyd cUD Cld Cmd cED Crd Cad Cåd Ccd
cRD cSD Csd Cjd cPD
根据代码点,排序如下:
Cad Cbd Ccd Cdd Ced Cfd Cgd Chd Cid Cjd Ckd Cld Cmd Cnd Cod Cpd Cqd
Crd Csd Ctd Cud Cvd Cxd Cyd Czd Cäd Cåd Cöd cAD cBD cCD cDD cED cFD
cGD cHD cID cJD cKD cLD cMD cND cOD cPD cQD cRD cSD cTD cUD cVD cXD
cYD cZD cÄD cÅD cÖD
但使用默认的 UCA 会使其按以下方式排序:
% ucsort /tmp/swedish_set | fmt
cAD Cad cÅD Cåd cÄD Cäd cBD Cbd cCD Ccd cDD Cdd cED Ced cFD Cfd cGD
Cgd cHD Chd cID Cid cJD Cjd cKD Ckd cLD Cld cMD Cmd cND Cnd cOD Cod
cÖD Cöd cPD Cpd cQD Cqd cRD Crd cSD Csd cTD Ctd cUD Cud cVD Cvd cXD
Cxd cYD Cyd cZD Czd
但在瑞典语中,是这样:
% ucsort --locale=sv /tmp/swedish_set | fmt
cAD Cad cBD Cbd cCD Ccd cDD Cdd cED Ced cFD Cfd cGD Cgd cHD Chd cID
Cid cJD Cjd cKD Ckd cLD Cld cMD Cmd cND Cnd cOD Cod cPD Cpd cQD Cqd
cRD Crd cSD Csd cTD Ctd cUD Cud cVD Cvd cXD Cxd cYD Cyd cZD Czd cÅD
Cåd cÄD Cäd cÖD Cöd
如果您希望大写字母先于小写字母排序,请执行以下操作:
% ucsort --upper-before-lower --locale=sv /tmp/swedish_set | fmt
Cad cAD Cbd cBD Ccd cCD Cdd cDD Ced cED Cfd cFD Cgd cGD Chd cHD Cid
cID Cjd cJD Ckd cKD Cld cLD Cmd cMD Cnd cND Cod cOD Cpd cPD Cqd cQD
Crd cRD Csd cSD Ctd cTD Cud cUD Cvd cVD Cxd cXD Cyd cYD Czd cZD Cåd
cÅD Cäd cÄD Cöd cÖD
定制排序
您还可以使用ucsort做很多其他事情。例如,以下是如何对英文标题进行排序:
% ucsort --preprocess='s/^(an?|the)s+//i' /tmp/titles
Anathem
The Book of Skulls
A Civil Campaign
The Claw of the Conciliator
The Demolished Man
Dune
An Early Dawn
The Faded Sun: Kesrith
The Fall of Hyperion
A Feast for Crows
Flowers for Algernon
The Forbidden Tower
Foundation and Empire
Foundation’s Edge
The Goblin Reservation
The High Crusade
Jack of Shadows
The Man in the High Castle
The Ringworld Engineers
The Robots of Dawn
A Storm of Swords
Stranger in a Strange Land
There Will Be Time
The White Dragon
通常,您需要 Perl 5.10.1 或更高版本才能运行该脚本。要获得语言环境支持,您必须安装可选的 CPAN 模块Unicode::Collate::Locale
。或者,您可以安装 Perl 的开发版本 5.13+,该版本标准地包含该模块。
调用约定
这是一个快速原型,因此ucsort基本没有文档记录。但这是它在命令行上接受的开关/选项的概要:
# standard options
--help|?
--man|m
--debug|d
# collator constructor options
--backwards-levels=i
--collation-level|level|l=i
--katakana-before-hiragana
--normalization|n=s
--override-CJK=s
--override-Hangul=s
--preprocess|P=s
--upper-before-lower|u
--variable=s
# program specific options
--case-insensitive|insensitive|i
--input-encoding|e=s
--locale|L=s
--paragraph|p
--reverse-fields|last
--reverse-output|r
--right-to-left|reverse-input
是的,好的:这实际上是我用于调用的参数列表Getopt::Long
,但你明白我的意思了。:)
如果您能弄清楚如何直接从 Python 调用 Perl 库模块而不调用 Perl 脚本,请务必这样做。我自己不知道怎么做。我很想学习如何做。
与此同时,我相信这个脚本将满足您的所有需求 —甚至更多! 我现在使用它来进行所有文本排序。它终于满足了我长久以来的需要。
唯一的缺点是该--locale
参数会导致性能下降,尽管它对于常规、非本地化但仍 100% 符合 UCA 的排序来说已经足够快了。由于它将所有内容加载到内存中,因此您可能不想在 GB 文档上使用它。我每天使用它很多次,最终获得合理的文本排序确实很棒。
解决方案 8:
要实现它,您需要阅读有关“Unicode 排序算法”的内容,请参阅
http://en.wikipedia.org/wiki/Unicode_collat ion_algorithm
http://www.unicode.org/unicode/reports/tr10/
示例实现在这里
http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/
解决方案 9:
最近我一直在使用 zope.ucol ( https://pypi.python.org/pypi/zope.ucol ) 来完成这项任务。例如,对德语 ß 进行排序:
>>> import zope.ucol
>>> collator = zope.ucol.Collator("de-de")
>>> mylist = [u"a", u'x', u'/u00DF']
>>> print mylist
[u'a', u'x', u'xdf']
>>> print sorted(mylist, key=collator.key)
[u'a', u'xdf', u'x']
zope.ucol 也包装了 ICU,因此可以作为 PyICU 的替代品。
解决方案 10:
它远非您用例的完整解决方案,但您可以查看effbot.org 上的unaccent.py脚本。它基本上的作用是从文本中删除所有重音符号。您可以使用该“净化”文本按字母顺序排序。(有关更详细的描述,请参阅此页面。)
解决方案 11:
Jeff Atwood 写了一篇关于自然排序顺序的很好的文章,其中他链接到一个脚本,该脚本基本上可以完成你所要求的操作。
无论如何,这不是一个简单的脚本,但是它确实起了作用。