Как отсортировать строки юникода в алфавитном порядке в 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. Это довольно экзотические особые случаи, которые редко имеют значение на практике.

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

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

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

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

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

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

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

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

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

Резюме и расширенный ответ:

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

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

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

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

Я вижу, что ответы уже проделали отличную работу, просто хотел указать на одну неэффективность кодирования в сортировке людей. Для применения выборочного преобразования 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, (Значения в диктовке, которую вы передаете для перевода [в отличие от ключей, которые должны быть ординалами], могут быть ординалами 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 - кроме как с помощью выноски оболочки или двустороннего канала. В связи с этим я предоставил вам полный рабочий скрипт 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 в основном не документирован. Но это его ОПИСАНИЕ того, какие переключатели / опции он принимает в командной строке:

    # 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 Аргумент приводит к снижению производительности, хотя это достаточно быстро для обычной, не локальной, но все же 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. Что он в основном делает, это удаляет все акценты из текста. Вы можете использовать этот "санированный" текст для сортировки по алфавиту. (Для лучшего описания см. Эту страницу.)

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

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

Другие вопросы по тегам