Python неправильно сортирует юникод.Strcoll не помогает - PullRequest
20 голосов
/ 05 августа 2010

У меня проблема с сортировкой списков с использованием юникодной сортировки в Python 2.5.1 и 2.6.5 в OSX, а также в Linux.

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 был сломанПробовал с различными типами переменных (например, строки, не кодированные в Юникоде).

Что я делаю не так?

С уважением, Томаш Копчук.

Ответы [ 6 ]

17 голосов
/ 05 августа 2010

Очевидно, что единственный способ сортировки для работы на всех платформах - это использовать библиотеку ICU с привязками PyICU ( PyICU на PyPI ).

В OS X: sudo port install py26-pyicu, ошибка, описанная здесь: 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: он потокобезопасен, поэтому не бесполезен при настройке локализаций по запросу.

5 голосов
/ 25 мая 2016

Вот как мне удалось правильно отсортировать персидский язык (без PyICU) (используя python 3.x):

Сначала задайте локаль (не забудьте импортировать локаль и платформа )

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, '')
4 голосов
/ 23 мая 2013

@ 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', 'ä']
4 голосов
/ 06 августа 2010

Просто чтобы добавить к расследованию tkopczuk: Это определенно ошибка gcc, по крайней мере для версии 4.2.1 в OS X 10.6.4. Это можно воспроизвести, вызвав C strcoll() непосредственно как в этом фрагменте .

РЕДАКТИРОВАТЬ: Все еще в той же системе, я нахожу, что для версий 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.
2 голосов
/ 10 июля 2013
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

(Ref .: http://docs.python.org/3.3/library/functools.html)

0 голосов
/ 05 августа 2010

В 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 не работает, если я что-то упустил

>>> 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)
...