【问题标题】:How do I sort unicode strings alphabetically in Python?如何在 Python 中按字母顺序对 unicode 字符串进行排序?
【发布时间】:2010-11-09 00:48:59
【问题描述】:

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

有这个库吗?我什么也找不到。排序最好有语言支持,这样它就知道 åäö 应该在瑞典语中的 z 之后排序,但 ü 应该按 u 排序,等等。因此,Unicode 支持几乎是一个要求。

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

【问题讨论】:

  • 请注意,这更依赖于语言环境:在瑞典语中(如您所说),“Ä”在“Z”之后,但在德语中,“Ä”通常排序为“AE”。
  • @Georg:你有什么理由为此提供赏金吗? locale.strcoll 答案在您需要使用用户的语言环境进行 Unicode 排序时是正确的,而 ICU 在您需要更多时回答您想要的(使用多个语言环境进行排序)。大多数时候,你想要locale.strcoll
  • @Glenn:我想知道locale.strcoll 的工作情况,尤其是 ICU 比 Python 函数做得更好的地方。基本上对这个问题多一些关注。
  • @Georg:我最近一直在使用 Unicode 排序算法,从我的回答中可以看出。例如,能够在需要时对--locale=de__phonebook 进行排序真是太好了。 Perl 模块通过了 UCA 测试套件,the script I provided 使得使用整个 UCA 以及它的所有选项包括语言环境变得更加容易。可能无法回答 the 问题,但它应该仍然非常有趣。如果你在瑞士,我相信你可以使用这种灵活性。 :)

标签: python sorting unicode internationalization collation


【解决方案1】:

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

更新:ICU和locale.strcoll排序的核心区别在于ICU使用完整的Unicode Collation Algorithm,而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']

【讨论】:

  • 这对 Python 2 和 Python 3 是否同样有效?我使用了 u0b34a0f6ae 的答案中的locale.strxfrm,它似乎可以工作,而且更加优雅,不需要任何额外的软件。
  • 不适用于我的 Python3,sudo pip3 install PyICU 无法安装,Python2 也是如此。
  • 我必须为 pyICU 安装 libicu-devel.x86_64 才能从 Pip 编译和安装。它可以工作,尽管最后一个“排序”命令的输出是:['a', '\xc3\xa4', '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 和其他回答者提出的问题:没有人知道“语言环境”还是不能胜任这项任务?

【讨论】:

  • 顺便说一句 1) 我不认为 locale.strxfrm 被 UTF-8 编码的 `str' 破坏了;我通过应用程序进行了基准测试并得出结论,在 unicode 对象上使用 cmp=strcoll 比将全部解码为 UTF-8 并使用 key=strxfrm 便宜
  • 顺便说一句 2) locale 模块只适用于您生成的语言环境(对于 Linux 机器),而不是任何任意语言环境。 “locale -a”会告诉你哪个
  • @Georg:我相信语言环境只支持简单的 substring->collat​​ing_element 映射。它不处理扩展(æ 排序为“ae”)、法语重音排序(字母从左到右排序,但重音从右到左排序)、重新排列以及可能更多的事情。详细信息(完整的 UCA 功能集):unicode.org/reports/tr10 和此处(区域设置排序):chm.tu-dresden.de/edv/manuals/aix/files/aixfiles/LC_COLLATE.htm
  • 要清楚地回答这个问题:是的,它由任务决定的。显然,完整的 Unicode 排序算法可以更好地处理某些特殊情况,但除非您已经知道可能不会注意到。
  • 这里最大的问题是:您必须为整个应用程序全局设置语言环境。 – 你不能只拿它来做手头的比较。
【解决方案3】:

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

【讨论】:

  • 这至少解决了一般问题。我想也可以创建排序列表的语言敏感版本。
  • 这不允许您指定语言环境,并且参考配置文件会导致 ValueError。
【解决方案4】:

总结和扩展答案:

locale.strcoll 在 Python 2 下,locale.strxfrm 实际上会解决问题,并且做得很好,前提是您安装了相关的语言环境。我也在 Windows 下对其进行了测试,其中语言环境名称令人困惑地不同,但另一方面,它似乎默认安装了所有支持的语言环境。

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

它还有很长的语言环境名称,因此您可以获得漂亮的语言环境显示名称,支持公历以外的其他日历(虽然我不确定 Python 接口是否支持)以及或多或少的大量其他日历模糊的语言环境支持。

总而言之:如果您想按字母顺序和区域设置排序,您可以使用locale 模块,除非您有特殊要求,或者还需要更多区域设置相关功能,例如分词器。

【讨论】:

    【解决方案5】:

    您可能也对pyuca感兴趣:

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

    虽然这肯定不是最准确的方法,但它是一种非常简单的方法,至少可以让它在某种程度上正确。它还击败了 web 应用程序中的语言环境,因为语言环境不是线程安全的,并且会在整个进程范围内设置语言设置。它也比依赖外部 C 库的 PyICU 更容易设置。

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

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

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

    【讨论】:

    • +1 为 pyuca。它相当快(3 秒排序 28000 个单词),是纯 python,不需要依赖。
    【解决方案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 方法的dict 将Unicode 序数(不是字符串)作为键,这就是为什么我们需要从原始字符到字符spec_dict 的重建步骤。 (您传递给翻译的 dict 中的值 [与键相反,键必须是序数] 可以是 Unicode 序数、任意 Unicode 字符串或 None 以删除相应的字符作为翻译的一部分,因此很容易指定“忽略一个某些用于排序目的的字符”、“将 ä 映射到 ae 以进行排序”等)。

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

    spec_dict = ''.maketrans(spec_dict)
    

    请参阅the docs,了解可以在 Python 3 中使用此 maketrans 静态方法的其他方式。

    【讨论】:

    • 这个方法很好,但不允许你在 az 和 b 之间放置 á
    【解决方案7】:
    【解决方案8】:

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

      完整的 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 大多没有(der)记录在案。但这是它在命令行上接受的开关/选项的概要:

          # 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 的参数列表,但你明白了。 :)

      如果你能弄清楚如何在不调用 Perl 脚本的情况下直接从 Python 调用 Perl 库模块,那么一定要这样做。我只是不知道自己怎么样。我很想知道怎么做。

      同时,我相信这个脚本会做你需要做的所有事情——还有更多!我现在将它用于所有的文本排序。它终于完成了我长期以来需要的工作。

      唯一的缺点是 --locale 参数会导致性能下降,尽管它对于常规的非语言环境但仍然 100% 符合 UCA 排序已经足够快了。由于它将所有内容加载到内存中,您可能不想在千兆字节文档上使用它。我每天使用它很多次,最后对文本进行合理的排序肯定很棒。

      【讨论】:

      • 为什么你会调用 Perl 脚本来做一些 Python 库可以做的事情?
      • 因为我不知道有 Python 库,这就是原因!
      • @Lennart:我真的更喜欢本机库,或者最多是链接到 C API 并动态加载的库(有时您需要)。我还没有发现各种 PyPerl 和 Inline::Perl 解决方案非常令人信服、健壮或灵活。或者其他的东西。由于某些原因,他们只是感觉不对。我上次尝试这个是在我需要良好的字符集检测时(唉,我从来没有得到过)。
      • 在 Python 中使用 Perl 简直上瘾了。
      • 哇。是的 - 对我来说看起来像 Perl,事实上我们看到现在有两种以上的方法来做事:) 但是从 Python 调用 C 通常并不意味着调用 Perl 会增加的依赖项和实际支持问题,所以它很难看到很多人要求这样做。
      【解决方案10】:

      对于您的用例来说,它远非一个完整的解决方案,但您可以查看来自 effbot.org 的 unaccent.py 脚本。它的基本作用是从文本中删除所有重音符号。您可以使用“净化”文本按字母顺序排序。 (有关更好的描述,请参阅this 页面。)

      【讨论】:

        【解决方案11】:

        Jeff Atwood 在Natural Sort Order 上写了一篇很好的帖子,其中他链接到了一个执行pretty much what you ask 的脚本。

        无论如何,这不是一个简单的脚本,但它可以解决问题。

        【讨论】:

          猜你喜欢
          • 2013-02-09
          • 2013-09-04
          • 1970-01-01
          • 2012-03-16
          • 1970-01-01
          • 2015-07-23
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多