【问题标题】:Python not sorting unicode properly. Strcoll doesn't helpPython 没有正确排序 unicode。 Strcoll 没有帮助
【发布时间】:2010-08-05 08:25:52
【问题描述】:

我在 OSX 以及 Linux 上的 Python 2.5.1 和 2.6.5 中使用 unicode 排序规则对列表进行排序时遇到问题。

import locale   
locale.setlocale(locale.LC_ALL, 'pl_PL.UTF-8')
print [i for i in sorted([u'a', u'z', u'ą'], cmp=locale.strcoll)]

应该打印的内容:

[u'a', u'ą', u'z']

而是打印出来:

[u'a', u'z', u'ą']

总结一下 - 看起来 strcoll 好像坏了。尝试了各种类型的变量(例如非 unicode 编码的字符串)。

我做错了什么?

最好的问候, 托马斯·科普祖克。

【问题讨论】:

  • locale.getlocale(LC_COLLATE) 在您的 setlocale 行之后返回什么?
  • locale 模块使用 C 库中的语言环境 API,因此如果出现错误,则必须在 C 库中。使用语言环境 de_DE.UTF-8 和字符串 ä 而不是 ą 的等效测试可以正常工作。即使我使用带有ą 的德语语言环境,顺序也是正确的,因此C 库中的波兰语言环境实现一定有问题。作为一种解决方法,您可以使用 unicodedata.normalize 将字符串转换为规范化形式 D,然后即使是幼稚的 strcmp 排序也应该可以工作。
  • 好的,我也对这个感兴趣。我尝试使用pl_PL.UTF-8de_DE.UTF-8,以及sort(key=locale.strxfrm),而不是在OS X 上使用strcoll,目前得到的结果不正确。用 de_DE.UTF8 刺痛ä 对我不起作用。
  • 适用于我的 Linux 但不适用于 Mac。也许 OS X 的排序规则表是错误的,还是什么? FWIW POSIX 语言环境对于 web 应用程序来说是狡猾的,因为它们是每个进程的,而不是线程安全的。
  • +1 适用于我在 Linux (Ubuntu) 上,但不适用于 Mac 和 FreeBSD。

标签: python unicode locale


【解决方案1】:

自 2012 年以来,有一个图书馆 natsort。它包括惊人的功能,如natsortedhumansorted。更重要的是,它们不仅适用于列表!。代码:

from natsort import natsorted, humansorted

lst = [u"a", u"z", u"ą"]
dct = {"ą": 1, "ż": 3, "Ż": 4, "b": 5}

lst_natsorted = natsorted(lst)
lst_humansorted = humansorted(lst)
dct_natsorted = dict(natsorted(dct.items()))
dct_humansorted = dict(humansorted(dct.items()))

print("List natsorted: ", lst_natsorted)
print("List humansorted: ", lst_humansorted, "\n")
print("Dictionary natsorted: ", dct_natsorted)
print("Dictionary humansorted: ", dct_humansorted)

输出:

List natsorted:  ['a', 'ą', 'z']
List humansorted:  ['a', 'ą', 'z']

Dictionary natsorted:  {'Ż': 4, 'ą': 1, 'b': 5, 'ż': 3}  
Dictionary humansorted:  {'ą': 1, 'b': 5, 'ż': 3, 'Ż': 4}

您可以看到排序字典时结果不同,但考虑到给定列表,两个结果都是正确的。

顺便说一句,这个库也很适合对包含数字的字符串进行排序:

from natsort import natsorted, humansorted

lst_mixed = ["a9", "a10", "a1", "c4", "c40", "c5"]

mixed_sorted = sorted(lst_mixed)
mixed_natsorted = natsorted(lst_mixed)
mixed_humansorted = humansorted(lst_mixed)

输出:

List with mixed strings sorted:  ['a1', 'a10', 'a9', 'c4', 'c40', 'c5']
List with mixed strings natsorted:  ['a1', 'a9', 'a10', 'c4', 'c5', 'c40']
List with mixed strings humansorted:  ['a1', 'a9', 'a10', 'c4', 'c5', 'c40']

【讨论】:

    【解决方案2】:

    只是为了补充 tkopczuk 的调查:这绝对是一个 gcc 错误,至少对于 OS X 10.6.4 上的 4.2.1 版本。可以直接调用Cstrcoll()in this snippet进行复现。

    编辑:仍然在同一个系统上,我发现对于 de_DE、fr_FR、pl_PL 的 UTF-8 版本,问题就在那里,但对于 fr_FR 和 de_DE 的 ISO-88591 版本,排序顺序是正确的。不幸的是,对于 OP,ISO-88592 pl_PL 也是错误的:

    The order for Polish ISO-8859 is:
    LATIN SMALL LETTER A
    LATIN SMALL LETTER Z
    LATIN SMALL LETTER A WITH OGONEK
    The LC_COLLATE culture and encoding settings were pl_PL, ISO8859-2.
    
    The order for Polish Unicode is:
    LATIN SMALL LETTER A
    LATIN SMALL LETTER Z
    LATIN SMALL LETTER A WITH OGONEK
    The LC_COLLATE culture and encoding settings were pl_PL, UTF8.
    
    The order for German Unicode is:
    LATIN SMALL LETTER A
    LATIN SMALL LETTER Z
    LATIN SMALL LETTER A WITH DIAERESIS
    The LC_COLLATE culture and encoding settings were de_DE, UTF8.
    
    The order for German ISO-8859 is:
    LATIN SMALL LETTER A
    LATIN SMALL LETTER A WITH DIAERESIS
    LATIN SMALL LETTER Z
    The LC_COLLATE culture and encoding settings were de_DE, ISO8859-1.
    
    The order for Fremch ISO-8859 is:
    LATIN SMALL LETTER A
    LATIN SMALL LETTER E WITH ACUTE
    LATIN SMALL LETTER Z
    The LC_COLLATE culture and encoding settings were fr_FR, ISO8859-1.
    
    The order for French Unicode is:
    LATIN SMALL LETTER A
    LATIN SMALL LETTER Z
    LATIN SMALL LETTER E WITH ACUTE
    The LC_COLLATE culture and encoding settings were fr_FR, UTF8.
    

    【讨论】:

    • 是否可以将/usr/share/locale/pl_PL.UTF-8/LC_COLLATE 反编译为某种可读形式?毕竟可能不是 gcc 错误,而是错误的排序规则表,正如@bobince 指出的那样。
    • 嗯,德语和法语的行为相同(即,带有变音符号的字符排序在“z”之后),所以不仅仅是波兰语排序表。我想知道它是否不只是选择 C ​​语言环境,或者可能是默认语言环境(我的是 en_GB —— 是你的 pl_PL 吗?)。无论如何,它显然在 C 库中,无论是在数据中还是在我无法分辨的代码中。
    • 是的,我的是 pl_PL。但是最好检查整理表,如果它们是洁净的,那么图书馆使用不同的语言环境设置就会出现问题。但我猜这是图书馆,因此各种操作系统上的问题。
    • 我不知道平台特定的排序规则表是如何制作的,除了它们应该是从公共区域设置存储库cldr.unicode.org 制作的。我对此研究得越多,我就越认为 C 库无论如何都是解释语言环境的一种非常简单的方法,而且你最好使用 ICU 进行严肃的工作。以上更多测试——de_DE 和 fr_FR ISO 语言环境没问题,但 pl_PL 对于 ISO 也有问题。
    • 这个问题似乎也适用于其他德语语言环境——即de_ATde_CH 以及de_DE——在它们的“独立”和UTF-8 版本中。 ISO8859-1ISO8859-15 看起来不错。操作系统:OS X 10.10.5 (Yosemite)
    【解决方案3】:

    显然,在所有平台上进行排序的唯一方法是使用带有 PyICU 绑定的 ICU 库 (PyICU on PyPI)。

    在 OS X 上:sudo port install py26-pyicu,注意此处描述的错误:https://svn.macports.org/ticket/23429(哦,使用 macports 的乐趣)。

    不幸的是,PyICUs 文档严重缺乏,但我设法找出了它是如何完成的:

    import PyICU
    collator = PyICU.Collator.createInstance(PyICU.Locale('pl_PL.UTF-8'))
    print [i for i in sorted([u'a', u'z', u'ą'], cmp=collator.compare)]
    

    给出:

    [u'a', u'ą', u'z']
    

    另一个专业人士 - @bobince:它是线程安全的,因此在设置请求明智的语言环境时并非无用。

    【讨论】:

    • 好问题,好答案——而且你领先所有人几步,难怪你在波兰:)。无论如何,这是我第二次看到 Python 依赖于底层 C 库的问题。你知道这些可以在哪里提出吗?
    • 我认为这可能是库本身的问题,而不是 Python 的问题。但正如 gnibbler 指出的那样 - 它恰好在某些操作系统中工作,所以也许,至少这个特定问题已经在某个时候得到修复。 OS X 以使用旧的 gcc 等而闻名,而我测试的另一个操作系统是 Fedora 8——它本身并不是很现代。我会在底层 C 库的邮件列表之一中提出这一点。干杯队友:)
    • 我同意。我做了一个 Gist gist.github.com/509520 并将它给几个人试用。我喜欢 i18n,但错误让它变得乏味。
    【解决方案4】:

    这是我如何正确地对波斯语进行排序(没有 PyICU)(使用 python 3.x):

    先设置locale(别忘了导入localeplatform

    if platform.system() == 'Linux':
        locale.setlocale(locale.LC_ALL, 'fa_IR.UTF-8')
    elif platform.system() == 'Windows':
       locale.setlocale(locale.LC_ALL, 'Persian_Iran.1256')
    else:
       pass (or any other OS)
    

    然后使用key排序:

    a = ['ا','ب','پ','ت','ث','ج','چ','ح','خ','د','ذ','ر','ز','ژ','س','ش','ص','ض','ط','ظ','ع','غ','ف','ق','ک','گ','ل','م','ن','و','ه','ي']
    
    print(sorted(a,key=locale.strxfrm))
    

    对象列表:

    a = [{'id':"ا"},{'id':"ب"},{'id':"پ"},{'id':"ت"},{'id':"ث"},{'id':"ج"},{'id':"چ"},{'id':"ح"},{'id':"خ"},{'id':"د"},{'id':"ذ"},{'id':"ر"},{'id':"ز"},{'id':"ژ"},{'id':"س"},{'id':"ش"},{'id':"ص"},{'id':"ض"},{'id':"ط"},{'id':"ظ"},{'id':"ع"},{'id':"غ"},{'id':"ف"},{'id':"ق"},{'id':"ک"},{'id':"گ"},{'id':"ل"},{'id':"م"},{'id':"ن"},{'id':"و"},{'id':"ه"},{'id':"ي"}]
    
    print(sorted(a, key=lambda x: locale.strxfrm(x['id']))
    

    终于可以返回语言环境了:

    locale.setlocale(locale.LC_ALL, '')
    

    【讨论】:

      【解决方案5】:
      import locale
      from functools import cmp_to_key
      iterable = [u'a', u'z', u'ą']
      sorted(iterable, key=cmp_to_key(locale.strcoll))  # locale-aware sort order
      

      (参考:http://docs.python.org/3.3/library/functools.html

      【讨论】:

        【解决方案6】:

        @gnibbler,使用带有 sorted() 函数的 PyICU 确实在 Python3 环境中工作。在浏览了 ICU API 文档和一些实验之后,我发现了 getSortKey() 函数:

        import PyICU
        collator = PyICU.Collator.createInstance(PyICU.Locale('de_DE.UTF-8'))
        sorted(['a','b','c','ä'],key=collator.getSortKey)
        

        产生所需的排序规则:

        ['a', 'ä', 'b', 'c']
        

        而不是不需要的排序规则:

        sorted(['a','b','c','ä'])
        ['a', 'b', 'c', 'ä']
        

        【讨论】:

          【解决方案7】:

          在 ubuntu lucid 上,使用 cmp 进行排序似乎可以正常工作,但我的输出编码错误。

          >>> import locale   
          >>> locale.setlocale(locale.LC_ALL, 'pl_PL.UTF-8')
          'pl_PL.UTF-8'
          >>> print [i for i in sorted([u'a', u'z', u'ą'], cmp=locale.strcoll)]
          [u'a', u'\u0105', u'z']
          

          除非我遗漏了什么,否则将 key 与 locale.strxfrm 一起使用是行不通的

          >>> print [i for i in sorted([u'a', u'z', u'ą'], key=locale.strxfrm)]
          Traceback (most recent call last):
            File "<stdin>", line 1, in <module>
          UnicodeEncodeError: 'ascii' codec can't encode character u'\u0105' in position 0: ordinal not in range(128)
          

          【讨论】:

          • 使用 strxfrm 您必须手动解码 Unicode 字符串 AFAIK。
          • @tkopczuk,很高兴找到一种使用key 进行排序的方法,因为cmp for sorted 在 Python3 中消失了
          • 使用提供的 functools.cmp_to_key 函数 (from functools import cmp_to_key) 似乎可以正常工作,例如:sorted([u'a', u'z', u'ą'], key=cmp_to_key(collator.compare))
          猜你喜欢
          • 1970-01-01
          • 2019-02-27
          • 2020-05-24
          • 2014-02-13
          • 2020-04-26
          • 2014-09-07
          • 2014-11-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多