Python未能正确排序Unicode,Strcoll无效

27 投票
8 回答
9251 浏览
提问于 2025-04-16 02:21

我在使用Python 2.5.1和2.6.5进行列表排序时遇到了问题,这个问题出现在OSX和Linux上,主要是关于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编码的字符串)。

我到底哪里做错了呢?

最好的祝福,
Tomasz Kopczuk。

8 个回答

5

这是我如何在不使用PyICU的情况下,正确排序波斯语的方法(使用Python 3.x):

首先要设置地区设置(别忘了导入 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)

然后使用关键字进行排序:

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, '')
7

补充一下tkopczuk的调查:这绝对是gcc的一个错误,至少在OS X 10.6.4上的版本4.2.1中是这样的。通过直接调用C语言中的strcoll()函数,可以重现这个问题,具体可以参考这个代码片段

编辑:在同一系统上,我发现对于UTF-8版本的de_DE、fr_FR和pl_PL,这个问题确实存在,但对于fr_FR和de_DE的ISO-88591版本,排序是正确的。不幸的是,对于提问者来说,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.
18

看来,要让排序在所有平台上都能正常工作,唯一的方法就是使用ICU库,并配合PyICU这个工具。

在OS X系统上,你可以用这个命令来安装:sudo port install py26-pyicu,不过要注意这里提到的一个bug:https://svn.macports.org/ticket/23429(使用macports的乐趣啊)。

可惜的是,PyICU的文档非常不完善,但我还是找到了使用的方法:

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提到的:它是线程安全的,所以在设置请求相关的地区时也不会出问题。

撰写回答