Как отсортировать строки в юникоде по алфавиту в Python?

Python сортирует по байтовому значению по умолчанию, что означает, что é появляется после z и других одинаково забавных вещей. Каков наилучший способ сортировки по алфавиту в Python?

Есть ли библиотека для этого? Я ничего не мог найти. Предпочтительная сортировка должна иметь языковую поддержку, поэтому она понимает, что åäö следует сортировать после z на шведском языке, но что ü следует сортировать по u и т.д. Поддержка Юникода, таким образом, в значительной степени является обязательным требованием.

Если для этого нет библиотеки, каков наилучший способ сделать это? Просто сделайте сопоставление от буквы к целочисленному значению и сопоставьте строку с целым списком с этим?

Ответ 1

Библиотека IBM ICU делает это (и многое другое). Он имеет привязки Python: PyICU.

Обновление. Основное различие в сортировке между ICU и locale.strcoll заключается в том, что ICU использует полный Unicode Collation Algorithm while 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']

Ответ 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 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)

Вопрос Леннарту и другим ответчикам: не знает ли кто-нибудь "локаль" или не справляется с этой задачей?

Ответ 3

Попробуйте Джеймс Таубер Алгоритм сортировки юникода Python. Возможно, это не так, как вам хочется, но, похоже, стоит посмотреть. Для получения дополнительной информации о проблемах см. этот пост Кристофера Ленца.

Ответ 4

Вас также может заинтересовать pyuca:

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

Хотя это, конечно, не самый точный способ, это очень простой способ, по крайней мере, получить его в порядке. Он также превосходит локаль в webapp, поскольку локаль не является потокобезопасной и устанавливает языковые настройки в масштабе всей системы. Это также проще настроить, чем PyICU, который опирается на внешнюю библиотеку C.

Я загрузил script в github, поскольку оригинал был на момент написания этой статьи, и мне пришлось прибегать к веб-кешам, чтобы получить его:

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

Я успешно использовал этот script для удобного сортировки немецкого/французского/итальянского текста в модуле plone.

Ответ 5

Сводный и расширенный ответ:

locale.strcoll в Python 2, и locale.strxfrm фактически решит проблему и сделает хорошую работу, предполагая, что у вас установлен соответствующий языковой стандарт. Я тестировал его также под Windows, где имена локалей смущены, но, с другой стороны, похоже, что все локали, которые поддерживаются, установлены по умолчанию.

ICU не всегда делает это лучше на практике, но, тем не менее, делает больше. В первую очередь это поддерживает разветкители, которые могут разделить тексты на разных языках на слова. Это очень полезно для языков, на которых нет разделителей слов. Вам нужно будет иметь состав слов для использования в качестве базы для расщепления, потому что это не включено.

У него также есть длинные имена для локалей, поэтому вы можете получать симпатичные отображаемые имена для локали, поддерживать другие календари, чем григорианские (хотя я не уверен, что интерфейс Python поддерживает это), а тонны и тонны других более или менее неясные локальные опоры.

Итак, все:. Если вы хотите сортировать в алфавитном порядке и зависящие от языка, вы можете использовать модуль locale, если у вас нет особых требований или также требуется больше зависимых от языка функций, например разделитель слов.

Ответ 6

Я вижу, что ответы уже сделали отличную работу, просто хотели указать на неэффективность кодирования в Human Sort. Чтобы применить выборочный перевод char -by- char в строку юникода 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, имеет порядковые номера Unicode (не строки) в качестве ключей, поэтому нам нужен этот шаг восстановления от исходного char -to- char spec_dict. (Значения в dict, которые вы передаете для перевода [в отличие от ключей, которые должны быть ординалами), могут быть ординалами Unicode, произвольными строками Unicode или None, чтобы удалить соответствующий символ как часть перевода, поэтому легко указать "игнорировать a определенный символ для сортировки", "карта ä для ae для целей сортировки" и т.п.).

В Python 3 вы можете упростить "перестройку", например:

spec_dict = ''.maketrans(spec_dict)

Смотрите документы для других способов использования этого статического метода maketrans в Python 3.

Ответ 7

Полное решение UCA

Простейший, простой и простой способ сделать это, чтобы сделать выноску в модуль библиотеки Perl, Unicode:: Collate:: Locale, который является подклассом стандартного модуля Unicode:: Collate. Все, что вам нужно, это передать конструктору значение локали "xv" для Швеции.

(Возможно, вы не можете это оценить по шведскому тексту, но поскольку Perl использует абстрактные символы, вы можете использовать любой код кода Unicode, какой бы вы ни хотели - независимо от платформы или сборки! Немногие языки предлагают такое удобство. Я упоминаю об этом, потому что Ive fight в последнее время проигрывает битва с Java над этой безумной проблемой.)

Проблема в том, что я не знаю, как получить доступ к модулю Perl из Python - отдельно, то есть от использования выноска оболочки или двустороннего канала. С этой целью Я предоставил вам полный рабочий script, называемый ucsort, который вы можете позвонить, чтобы сделать то, что вы просили, идеальная легкость.

Этот script на 100% совместим с полным алгоритмом сортировки Unicode, при этом поддерживаются все варианты пошива!! И если у вас есть дополнительный модуль, установленный или запущенный 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 ä å ö

что неверно в книге everybodys. Используя мой script, который использует алгоритм сортировки 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 или выше для запуска script в целом. Для поддержки языкового стандарта вы должны либо установить дополнительный модуль CPAN Unicode::Collate::Locale. В качестве альтернативы вы можете установить версии разработки Perl, 5.13+, которые обычно включают этот модуль.

Соглашения о вызовах

Это быстрый прототип, поэтому ucsort в основном документируется un (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 script, это все равно. Я просто не знаю, как себя. Мне нравится учиться.

Тем временем, я считаю, что этот script будет делать то, что вам нужно сделать во всех его особенностях - и больше!. Теперь я использую это для всей сортировки текста. Это, наконец, делает то, что нужно для долгого, долгого времени.

Единственным недостатком является то, что аргумент --locale приводит к тому, что производительность снижается по трубам, хотя его достаточно быстро для обычной, не локальной , но все еще 100% -ной совместимости с UCA. Поскольку он загружает все в памяти, вы, вероятно, не хотите использовать это в документах с гигабайтом. Я использую его много раз в день, и он уверен, что наконец-то неплохо будет отсортировать текст.

Ответ 9

В последнее время я использовал 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.

Ответ 11

Это далеко не полное решение для вашего случая использования, но вы можете взглянуть на unaccent.py script from effbot.org. В основном это удаление всех акцентов из текста. Вы можете использовать этот "дезинфицированный" текст для сортировки по алфавиту. (Для лучшего описания см. эта страница.)