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

时间:2009-07-08 12:59:09

标签: python sorting unicode internationalization collation

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

这是否有图书馆?我找不到任何东西。最好排序应该有语言支持所以它理解åäö应该用瑞典语中的z排序,但是ü应该用u等排序。因此,Unicode支持是非常必要的。

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

11 个答案:

答案 0 :(得分:69)

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

更新:ICU与locale.strcoll之间排序的核心差异在于ICU使用完整Unicode Collation Algorithmstrcoll使用ISO 14651。< / p>

这两个算法之间的差异简要总结如下: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']

答案 1 :(得分: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和其他回答者的问题:没有人知道'locale'或者不能完成这项任务吗?

答案 2 :(得分:9)

尝试James Tauber的Python Unicode Collation Algorithm。它可能不会完全按照您的意愿完成,但似乎值得一看。有关这些问题的更多信息,请参阅Christopher Lenz的this post

答案 3 :(得分:8)

您可能也对 pyuca

感兴趣

http://jtauber.com/blog/2006/01/27/python_unicode_collation_algorithm/

虽然它肯定不是最精确的方式,但这是一种非常简单的方法,至少可以让它有点正确。它还在webapp中击败语言环境,因为语言环境不是线程安全的,并在进程范围内设置语言设置。它比PyICU更容易设置,PyICU依赖于外部C库。

我将脚本上传到github,因为在写这篇文章的时候原版已经关闭了,我不得不求助于网络缓存来获取它:

https://github.com/href/Python-Unicode-Collation-Algorithm

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

答案 4 :(得分:7)

摘要和扩展答案:

在Python 2下的

locale.strcolllocale.strxfrm实际上将解决问题,并且做得很好,假设你安装了有问题的语言环境。我也在Windows下对它进行了测试,其中区域设置名称容易混淆,但另一方面,它似乎默认安装了所有支持的语言环境。

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

它还具有很长的语言环境名称,因此您可以获得该语言环境的漂亮显示名称,支持除Gregorian之外的其他日历(虽然我不确定Python界面是否支持)以及吨或吨其他或多或少模糊的语言环境支持。

总而言之:如果要按字母顺序和区域设置进行排序,可以使用locale模块,除非您有特殊要求,或者还需要更多与语言环境相关的功能,就像分词器一样。

答案 5 :(得分:6)

我看到答案已经做得很好,只想在Human Sort中指出一个编码效率低下的问题。要将选择性char-by-char转换应用于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重建步骤的原因。 (传递给翻译的dict中的值[与键相对,必须是序数]可以是Unicode序号,任意Unicode字符串或None,以删除相应的字符作为翻译的一部分,因此很容易指定“忽略a用于分类目的的某些字符“,”将ä映射到ae以进行分类“等等。

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

spec_dict = ''.maketrans(spec_dict)

有关在Python 3中使用此maketrans静态方法的其他方法,请参阅the docs

答案 6 :(得分:2)

完整的UCA解决方案

执行此操作的最简单,最简单,最直接的方法是对Perl库模块Unicode::Collate::Locale进行调用,该模块是标准Unicode::Collate模块的子类。您所需要做的就是为瑞典传递构造函数"xv"的语言环境值。

(你可能不会对瑞典文本有所了解,但是因为Perl使用抽象字符,你可以使用任何Unicode代码点 - 无论是平台还是构建!很少有语言提供这样的便利。我提到它是因为我'我最近在这个令人抓狂的问题上与Java打了一场失败的战斗。)

问题在于我不知道如何从Python访问Perl模块 - 除此之外,即使用shell标注或双面管道。为此,I have therefore provided you with a complete working script called ucsort你可以打电话来完全轻松地完成你所要求的。

此脚本100%符合完整Unicode Collation Algorithm ,支持所有剪裁选项!如果您安装了可选模块或运行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标准排序。由于它将所有内容加载到内存中,您可能不希望在千兆字节文档上使用它。我每天都会多次使用它,并且最终确保它有很好的文本排序。

答案 7 :(得分:1)

答案 8 :(得分: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。

答案 9 :(得分:0)

杰夫阿特伍德在Natural Sort Order上写了一篇好文章,其中链接到了pretty much what you ask的脚本。

无论如何,这不是一个微不足道的剧本,但它可以解决问题。

答案 10 :(得分:0)

对于您的用例,它远非完整的解决方案,但您可以查看来自effbot.org的unaccent.py脚本。它基本上做的是删除文本中的所有重音。您可以使用“已清理”文本按字母顺序排序。 (有关更好的说明,请参阅this页。)