Python未正确排序unicode。Strcoll没有';帮不上忙

Python未正确排序unicode。Strcoll没有';帮不上忙,python,unicode,locale,Python,Unicode,Locale,在OSX和Linux上,在Python2.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'ą'] 总结一

在OSX和Linux上,在Python2.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好像坏了。尝试使用各种类型的变量(fe.非unicode编码字符串)

我做错了什么

致以最良好的祝愿,
Tomasz Kopczuk。

在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']
在locale.strxfrm中使用key不起作用,除非我遗漏了什么

>>> 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)
>>打印[i for i in sorted([u'a',u'z',u'ą',key=locale.strxfrm)]
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
UnicodeEncodeError:“ascii”编解码器无法对位置0中的字符u'\u0105'进行编码:序号不在范围内(128)

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

在OSX上:
sudo端口安装py26-pyicu
,注意这里描述的bug:(哦,使用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']

另一个优点是:它是线程安全的,所以在设置请求相关的区域设置时不会毫无用处。

只是为了补充tkopczuk的调查:这肯定是一个gcc错误,至少对于OS X 10.6.4上的版本4.2.1是如此。它可以通过直接调用C
strcoll()
来复制

编辑:仍然在同一个系统上,我发现对于UTF-8版本的de_-de、fr_-fr、pl_-pl,问题是存在的,但是对于ISO-88591版本的fr_-fr和de_-de,排序顺序是正确的。不幸的是,对于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.

@gnibbler,使用PyICU和sorted()函数在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', 'ä']
而不是不需要的排序规则:

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

(参考:)

以下是我如何正确排序波斯语(不使用PyICU)(使用python 3.x):

首先设置区域设置(不要忘记导入区域设置和平台)

然后使用以下键进行排序:

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

在setlocale行之后,
locale.getlocale(LC\u COLLATE)
返回什么?
locale
模块使用C库中的locale API,因此如果出现错误,它必须在C库中。使用locale
de_de.UTF-8
和string
ä
代替
ą
的等效测试可以正常工作。即使我将德语区域设置与
ą
一起使用,顺序也是正确的,因此C库中的波兰语区域设置实现肯定有问题。作为一种解决方法,您可以使用
unicodedata将字符串转换为规范化表单D。规范化
,那么即使是简单的
strcmp
排序也应该可以。好的,我也对这一点感兴趣。我尝试了
pl\u pl.UTF-8
de\u de.UTF-8
,也尝试了
sort(key=locale.strxfrm)
而不是在OSX上使用
strcoll
,目前我得到的结果不正确。带有de_de.UTF8的Sting
ä
对我不起作用。在Linux上对我起作用,但在Mac上不起作用。也许OSX的排序表错了,或者什么的?FWIW POSIX语言环境对于Web应用程序来说是不可靠的,因为它们是每个进程的,而不是线程安全的。+1在Linux(Ubuntu)上对我有效,但在Mac和FreeBSD上都无效。使用strxfrm,你必须手动解码unicode字符串。@tkopczuk,找到一种使用
key
进行排序的方法会很好,因为
cmp
对于
sorted
已经在python3中消失了,它似乎与提供的functools一起工作得很好。cmp_to_key函数(
从functools导入cmp_to_key
),比如:
排序([u'a',u'z',u'261;],key=cmp_to_key(collator.compare))/code>好问题,回答得很好——你比所有人都领先几步,这也难怪你在波兰:)。无论如何,这是我第二次看到Python依赖底层C库的问题。你知道这些可以在哪里提出吗?我认为这可能是库本身的问题,而不是Python的问题。但正如gnibbler所指出的,它恰好在某些操作系统中工作,因此,至少这个特定的问题在某个时候已经得到了解决。OSX以使用旧的gcc和so而闻名,我测试的另一个操作系统是Fedora8,它本身并不十分现代。我会在底层C库的邮件列表中提出这个问题。干杯,伙计:)我同意。我做了一个要点,并将它交给一些人来尝试。我喜欢i18n,但这些bug让它变得单调乏味。是否可以将
/usr/share/locale/pl\u pl.UTF-8/LC\u COLLATE
反编译成某种可读形式?说到底,这可能不是gcc的错误,但正如@bobince所指出的那样,排序表是错误的。我对德语和法语也有同样的行为(即,带变音符号的字符排序在“z”之后),所以这不仅仅是波兰语的排序表。我想知道它是不是只选择了C语言环境,还是默认语言环境(我的是en_GB——你的是pl_pl?)。无论如何,它显然在C库中,无论是在数据中还是在代码中,我都说不出来。是的,我的是pl_pl。但是最好检查一下排序规则表,如果它们是犹太的,那么库使用的不同语言环境设置就会出问题。但我想这是库的问题,因此在各种操作系统上都会出现问题。我不知道特定于平台的排序规则表是如何生成的,只是它们应该是从公共区域设置存储库生成的。我越是深入研究这个问题,我就越觉得C库是一种非常简单的方法来解释语言环境,而且你最好使用ICU来完成严肃的工作。以上是更多的测试——de_de和fr_fr ISO语言环境是可以的,但pl_pl是一个