如何在Python中按字母顺序对unicode字符串排序?


97

Python默认情况下按字节值排序,这意味着é在z和其他同样有趣的事情之后。在Python中按字母顺序排序的最佳方法是什么?

有图书馆吗?我什么都找不到。最好是排序应该有语言支持,因此它理解åäö应该用瑞典语在z之后排序,但是ü应该用u进行排序,依此类推。因此,Unicode支持非常必要。

如果没有它的库,什么是最好的方法?只需将字母映射到整数值,然后将字符串映射到整数列表?


11
请注意,这甚至取决于语言环境:在瑞典语中(如您所述),“Ä”在“ Z”之后,但是在德语中,“Ä”通常被排序为“ AE”。
balpha

@乔治:您是否有理由为此悬赏?locale.strcoll当您需要使用用户的语言环境进行Unicode排序时,答案是正确的;当ICU需要的内容更多时,ICU会回答您想要的内容(使用多个语言环境的排序规则)。大多数时候,您想要locale.strcoll
Glenn Maynard

@Glenn:我想知道效果如何locale.strcoll,尤其是ICU比Python功能更好。基本上,这个问题需要更多注意。
GeorgSchölly

1
@Georg:从我的答案中可以看出,最近我在Unicode排序算法上玩了很多。能够(例如)--locale=de__phonebook在需要时进行排序确实很棒。Perl模块通过了UCA测试套件,而我提供的脚本使整个UCA及其所有选项(包括语言环境)的播放变得更加容易仅从命令行即可。可能不会回答问题,但它仍然应该是非常有趣的。如果您在瑞士,我相信您可以使用这种灵活性。:)
tchrist 2011年

Answers:


75

IBM的ICU库可以做到这一点(还有更多)。它具有Python绑定:PyICU

更新:在ICU之间进行排序的核心区别locale.strcoll在于,ICU使用完整的Unicode排序算法,strcoll使用ISO 14651

此处简要总结了这两种算法之间的区别:http : //unicode.org/faq/collat​​ion.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']

这对于Python 2和Python 3是否起作用?我locale.strxfrm从u0b34a0f6ae的答案中使用了它,它似乎可以正常工作并且更加优雅,并且不需要任何其他软件。
2015年

对我来说,sudo pip3 install PyICU它不适用于Python3 ,无法安装,对于Python2也是如此。
imrek '16

我必须为pyICU安装libicu-devel.x86_64,才能从Pip进行编译和安装。尽管最后一个“已排序”命令的输出为:['a','\ xc3 \ xa4','b','c'],但它仍然有效
Mike Stoddart

53

我没有在答案中看到这一点。我的应用程序使用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和其他回答者的问题:没有人知道“语言环境”吗?还是不符合这项任务?


顺便说一句:1)我不认为locale.strxfrm对于UTF-8编码的'str'是坏的;我按应用程序进行了基准测试,得出的结论是,在unicode对象上使用cmp = strcoll比将所有内容解码为UTF-8和使用key = strxfrm便宜
u0b34a0f6ae 2009年

6
顺便说一句2)语言环境模块仅适用于您生成的语言环境(对于Linux机器),而不适用于任何任意语言环境。“ locale -a”会告诉您
u0b34a0f6ae 2009年

6
@Georg:我相信语言环境仅支持简单的substring-> collat​​ing_element映射。它不处理扩展(重排为“ ae”),法语重音排序(字母从左至右排序,但重音从右至左),重排等操作。这里详细(全UCA功能集):unicode.org/reports/tr10和这里(现场核对):chm.tu-dresden.de/edv/manuals/aix/files/aixfiles/LC_COLLATE.htm
拉法尔Dowgird

2
为了清楚地回答这个问题:是的,它能够胜任工作。显然,在某些特殊情况下,完整的Unicode排序算法可以更好地处理,但是除非您已经知道机会,否则您不会注意到。
Lennart Regebro

1
这里最大的问题是:您必须为整个应用程序全局设置语言环境。–您不能只是为了进行比较而使用它。
罗伯特·西默


8

您可能也对pyuca感兴趣:

http://jtauber.com/blog/2006/01/27/python_unicode_collat​​ion_algorithm/

尽管肯定不是最精确的方法,但至少可以使它正确一些,这是一种非常简单的方法。由于语言环境不是线程安全的,因此它还会在Web应用程序中击败语言环境,并在整个过程中设置语言设置。它比依赖外部C库的PyICU设置起来容易。

我将脚本上传到github上,因为在撰写本文时原始脚本已关闭,因此我不得不依靠网络缓存来获取它:

https://github.com/href/Python-Unicode-Collat​​ion-Algorithm

我成功地使用此脚本对plone模块中的德语/法语/意大利语文本进行了合理排序。


pyuca +1。它相当快(3秒排序28000个单词),是纯python,不需要依赖。
michaelmeyer

7

摘要和扩展答案:

locale.strcoll在Python 2下,并locale.strxfrm假设您已经安装了有问题的语言环境,实际上可以解决问题,并且表现出色。我也在Windows下进行了测试,这在令人困惑的语言环境名称方面是不同的,但另一方面,默认情况下似乎安装了所有受支持的语言环境。

ICU不一定在实践中会做得更好,但是会做得更多。最值得注意的是,它支持可将不同语言的文本拆分为单词的拆分器。这对于没有单词分隔符的语言非常有用。您需要有一个语料库来用作拆分的基础,因为虽然不包括在内。

它还具有较长的语言环境名称,因此您可以获得语言环境的漂亮显示名称,支持除Gregorian之外的其他日历(尽管我不确定Python接口是否支持该日历),以及成吨的其他或多或少晦涩的语言环境支持。

因此,总而言之:如果您希望按字母顺序和语言环境进行排序,则可以使用该locale模块,除非您有特殊要求,或者还需要更多的语言环境相关功能,例如单词拆分器。


6

我看到答案已经做了出色的工作,只想指出“ 人类排序”中的编码效率低下。要将选择性逐字符转换应用于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方法的dict将Unicode序号(不是字符串)作为键,这就是为什么我们需要从原始char-to-char进行重建的原因spec_dict。(传递给dict的dict中的值(相对于键,键必须为序数)可以是Unicode序数,任意Unicode字符串,也可以是None来删除相应字符作为翻译的一部分,因此很容易指定“忽略一个某些字符以进行排序”,“将ä映射到ae以进行排序”等)。

在Python 3中,您可以更简单地完成“重建”步骤,例如:

spec_dict = ''.maketrans(spec_dict)

请参阅的文档你可以使用这个其他方式maketrans在Python 3静态方法。


这个方法是好的,但不允许你把AZ和B之间的
巴尼


1

最近,我一直在使用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。


1

完整的UCA解决方案

执行此操作的最简单,最简单,最直接的方法是对Perl库模块Unicode :: Collat​​e :: Locale进行调出,它是标准Unicode :: Collat​​e模块的子类。您需要做的就是将"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
Foundations 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的排序足够快。由于它将所有内容加载到内存中,因此您可能不想在千兆字节的文档上使用它。我每天使用它很多次,并且可以确保最后一次理智的文本排序非常好。


2
到底为什么要调用Perl脚本来做一些Python库呢?
Lennart Regebro

2
因为我不知道有一个Python库,这就是为什么!
tchrist 2011年

@Lennart:我真的更喜欢本机库,或者最多是链接到C API并动态加载(有时需要)的库。我还没有发现各种PyPerl和Inline :: Perl解决方案非常有说服力,强大或灵活。或者其他的东西。他们只是出于某些原因感到不对。我上次尝试此操作是在需要良好的字符集检测时((,我从未有过)。
tchrist 2011年

4
在Python中使用Perl只是上瘾。
Utku Zihnioglu 2011年

1
哇。是的-在我看来就像Perl,实际上我们发现现在有两种以上的工作方式:)但是从Python调用C通常并不意味着像调用Perl那样会带来更多的依赖关系和实际支持问题,因此以这种方式很难看到很多要求。
nealmcb 2011年

0

这是远从你的使用情况的完整解决方案,但你可以看看的unaccent.py从effbot.org脚本。它的主要作用是删除文本中的所有重音。您可以使用“经过消毒的”文本按字母顺序排序。(有关详细说明,请参阅页面。)


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.