Как сортировать строки unicode в алфавитном порядке в Python?

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

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

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

11 ответов


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

обновление: основная разница в сортировке между ICU и locale.strcoll это то, что ICU использует полный Алгоритм Сортировки Unicode пока 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']

Я не вижу в ответах. Мое приложение сортирует в соответствии с локалем, используя стандартную библиотеку 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's 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)

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


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


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

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

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

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

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

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


краткий и развернутый ответ:

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

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

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

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


Я вижу, что ответы уже сделали отличную работу, просто хотел указать на одну неэффективность кодирования в Человеческого Рода. Чтобы применить выборочный перевод 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)

в dict вы переходите к translate метод имеет ординалы Юникода (не строки) в качестве ключей, поэтому нам нужен этот шаг перестройки из исходного char-to-char spec_dict. (Значения в dict, которые вы передаете для перевода [в отличие от ключей, которые должны быть ординалами], могут быть ординалами Unicode, произвольными строками Unicode или None для удаления соответствующего символа как части перевода, поэтому легко указать "игнорировать определенный символ для целей сортировки", "сопоставить ä с ae для целей сортировки" и любить.)

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

spec_dict = ''.maketrans(spec_dict)

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


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

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

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

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

этот скрипт на 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 ä å ö

что неверно по всеобщему мнению. Используя мой скрипт, который использует алгоритм сортировки 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 или лучше запустить скрипт в целом. Для поддержки языкового стандарта необходимо либо установить дополнительный модуль 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, обязательно сделайте это. Я и сам не знаю, как. Я бы хотел научиться.

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

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


чтобы реализовать его, вам нужно будет прочитать об " алгоритме сортировки Unicode" видеть http://en.wikipedia.org/wiki/Unicode_collation_algorithm

http://www.unicode.org/unicode/reports/tr10/

пример реализации здесь

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


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


Джефф Этвуд написал хороший пост на Естественный Порядок Сортировки, в нем он связан со скриптом, который делает в значительной степени то, что вы просите.

Это не тривиальный сценарий, во всяком случае, но он делает трюк.


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