Python默认按字节值排序,这意味着é会排在z和其他同样有趣的字符之后。在Python中按字母顺序排序的最佳方法是什么?
是否有适用于此的库?我找不到任何东西。最好具备语言支持,以便它能够理解在瑞典语中,åäö应该在z之后排序,但ü应该按u排序等。因此,Unicode支持几乎是必需的。
如果没有库可用,那么最佳方法是什么?只需从字母到整数值的映射,并使用它将字符串映射到整数列表中吗?
Python默认按字节值排序,这意味着é会排在z和其他同样有趣的字符之后。在Python中按字母顺序排序的最佳方法是什么?
是否有适用于此的库?我找不到任何东西。最好具备语言支持,以便它能够理解在瑞典语中,åäö应该在z之后排序,但ü应该按u排序等。因此,Unicode支持几乎是必需的。
如果没有库可用,那么最佳方法是什么?只需从字母到整数值的映射,并使用它将字符串映射到整数列表中吗?
IBM的ICU库可以实现这个功能(以及更多)。它有Python绑定:PyICU。
更新: ICU和locale.strcoll
之间排序的核心差异在于,ICU使用完整的Unicode排序算法,而strcoll
使用ISO 14651。
这两种算法之间的区别在此简要概述。这些都是相当奇特的特殊情况,在实践中很少会有影响。
>>> 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']
locale.strxfrm
,它似乎可以工作,并且更加优雅,不需要任何额外的软件。 - supsudo pip3 install PyICU
安装失败,Python2也是如此。 - imrek我在回答中没有看到这个内容。我的应用程序使用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”,或者它不能胜任这个任务?locale.strxfrm
对于UTF-8编码的str
有问题;我对我的应用程序进行了基准测试,并得出结论,在Unicode对象上使用cmp=strcoll
比将所有内容解码为UTF-8并使用key=strxfrm
更便宜。 - u0b34a0f6ae你可能也对pyuca感兴趣:
http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/
虽然这肯定不是最精确的方法,但至少它是一个非常简单的方法,可以让它变得更为准确。在Web应用程序中,它也比区域设置好用,因为区域设置不是线程安全的,并且将语言设置进程范围内。相比于依赖外部C库的PyICU,它也更易于设置。
我将脚本上传到了github,因为原始版本正在撰写时下线,所以我不得不使用Web缓存获取它:
https://github.com/href/Python-Unicode-Collation-Algorithm
我成功地使用这个脚本在一个plone模块中对德语/法语/意大利语文本进行了合理的排序。
总结与扩展回答:
在Python 2中,locale.strcoll
和locale.strxfrm
实际上可以解决这个问题,并且做得非常好,假设您已安装所需的语言环境。我也在Windows下进行了测试,在那里语言环境的名称令人困惑,但另一方面它似乎默认安装了所有支持的语言环境。
ICU
在实践中不一定做得更好,但它确实做了很多其他事情。最明显的是,它支持将文本拆分成单词并支持不同语言的拆分器。这对于没有词分隔符的语言非常有用。您需要有一个单词库作为拆分的基础,因为它不包括在内。
它还具有语言环境的长名称,因此您可以获得漂亮的显示名称,支持除公历以外的其他日历(尽管我不确定Python界面是否支持),以及大量的其他更或者说不太常见的区域设置功能。
所以总之:如果您想按字母顺序排序并依赖于语言环境,则可以使用locale
模块,除非您有特殊要求,或者还需要更多的与语言环境相关的功能,例如单词拆分器。
我看到回答已经做得很好了,只想指出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序数(而不是字符串)作为键,这就是为什么我们需要从原始字符到字符的spec_dict
进行重建的原因。 (您传递给翻译的字典中的值[与必须是序数的键相反]可以是Unicode序数,任意Unicode字符串或None以删除相应字符作为翻译的一部分,因此很容易指定“忽略某些字符进行排序”,“将ä映射到ae以进行排序”等)。
在Python 3中,您可以更简单地获得“重建”步骤,例如:
spec_dict = ''.maketrans(spec_dict)
请参阅Python 3中使用此maketrans
静态方法的其他方法,请查看文档。
最简单、最容易、最直接的方法是调用Perl库模块Unicode::Collate::Locale,它是标准Unicode::Collate模块的子类。你只需要将语言环境值设置为"xv"
即可,适用于瑞典。
(对于瑞典文本,您可能不会特别欣赏此功能,但由于Perl使用抽象字符,因此您可以使用任何Unicode代码点,无论平台或构建方式如何!很少有语言提供这样的便利。我提到这一点是因为最近我一直在与Java为这个令人发狂的问题而斗争。)
问题在于我不知道如何从Python中访问Perl模块,除了使用shell调用或双向管道。为此,我已经提供了一个名为ucsort的完整工作脚本,您可以调用它以轻松实现您所要求的功能。
该脚本完全符合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兼容排序来说已经足够快了。由于它会将所有内容加载到内存中,因此您可能不想在千兆字节的文档上使用它。我每天使用它很多次,拥有理智的文本排序真是太棒了。
最近我一直在使用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 的替代选择。
要实现它,您需要阅读有关“Unicode排序算法”的相关资料,请参见http://en.wikipedia.org/wiki/Unicode_collation_algorithm
http://www.unicode.org/unicode/reports/tr10/
这里有一个示例实现
http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/
locale.strcoll
答案是正确的;当你需要更多时(使用多种区域设置进行排序),ICU答案是正确的。但大多数情况下,你都需要使用locale.strcoll
。 - Glenn Maynardlocale.strcoll
的工作效果如何,尤其是 ICU 在哪些方面比 Python 函数做得更好。基本上就是希望这个问题能够得到更多的关注。 - Georg Schölly--locale=de__phonebook
进行排序是非常好的。Perl模块通过了UCA测试套件,而我提供的脚本使得从命令行轻松玩转整个UCA及其所有选项,包括语言环境变得更加容易。虽然它可能不能回答这个问题,但它仍然非常有趣。如果你在瑞士,我相信你会喜欢这种灵活性的。 :) - tchrist