Как в Python отсортировать строки Unicode по алфавиту?

98

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

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

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

Леннарт Регебро
источник
11
Обратите внимание, что это еще больше зависит от локали: в шведском (как вы утверждаете) "Ä" стоит после "Z", но в немецком "Ä" обычно сортируется как "AE".
balpha 08
@Georg: Была ли причина, по которой вы назначили награду за это? locale.strcollОтвет является правильным , когда вам нужно Unicode сортировки с использованием локали пользователя, и ICU ответ , что вы хотите , когда вам нужно больше , чем (сверку с использованием более одного языкового стандарта). В большинстве случаев вы хотите locale.strcoll.
Гленн Мейнард
@Glenn: Я хотел знать, насколько хорошо locale.strcollработает и особенно то, что ICU лучше, чем функция Python. В основном еще немного внимания к вопросу.
Георг Шелли
1
@Georg: В последнее время я много играл с алгоритмом сортировки Unicode, как вы можете видеть из моего ответа. Например, действительно здорово иметь возможность сортировать, --locale=de__phonebookкогда вам это нужно. Модуль Perl проходит тестовый набор UCA, а предоставленный мной сценарий значительно упрощает работу со всем UCA, а также со всеми его параметрами, включая локали, прямо из командной строки. Не могу ответить на вопрос, но все равно должно быть очень интересно. Если вы находитесь в Швейцарии, я уверен, что вы могли бы воспользоваться этой гибкостью. :)
tchrist

Ответы:

75

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

Обновить : основное различие в сортировке между ICU и 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 2 и Python 3? Я использовал locale.strxfrmответ u0b34a0f6ae, и, похоже, он работает, намного элегантнее и не требует дополнительного программного обеспечения.
вс,
У меня не работает с Python3, sudo pip3 install PyICUне устанавливается, как и с Python2.
imrek
Мне пришлось установить libicu-devel.x86_64 для компиляции и установки pyICU из Pip. Это работает, хотя результат последней команды 'sorted': ['a', '\ xc3 \ xa4', 'b', 'c']
Майк Стоддарт,
53

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

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

u0b34a0f6ae
источник
Кстати, 1) я не думаю, что locale.strxfrm не работает для str в кодировке UTF-8; Я протестировал приложение и пришел к выводу, что использование cmp = strcoll для объектов Unicode дешевле, чем декодирование всего в UTF-8 и использование key = strxfrm
u0b34a0f6ae
6
Кстати 2) Модуль локали будет работать только с вашими сгенерированными локали (для Linux), а не с любой произвольной локалью. "locale -a" скажет вам, что именно
u0b34a0f6ae
6
@Georg: я считаю, что локаль поддерживает только простое сопоставление substring-> collating_element. Он не обрабатывает такие вещи, как расширение (æ отсортировано как «ae»), сортировка по французскому акценту (буквы отсортированы слева направо, но акценты справа налево), перестановка и, возможно, еще несколько. Подробности здесь (полный набор функций UCA): unicode.org/reports/tr10 и здесь (сопоставление локалей
Rafał Dowgird,
3
Для того, чтобы четко ответить на вопрос: Да, это до задачи. Очевидно, есть некоторые особые случаи, когда полный алгоритм сортировки Unicode обрабатывает лучше, но, если вы еще не знали, скорее всего, вы не заметите.
Леннарт Регебро
1
Самая большая проблема здесь в том, что вы должны установить локаль глобально для всего приложения. - Нельзя просто иметь это под рукой для сравнения.
Роберт Симер
9

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

Винай Саджип
источник
Это, по крайней мере, решает общую проблему. Думаю, можно было бы создать и языковые версии списка сопоставления.
Леннарт Регебро 08
Это не позволяет указать языковой стандарт, а ссылочный файл конфигурации вызывает ошибку ValueError.
thebjorn
8

Возможно, вас заинтересует пьюка :

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

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

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

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

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

href_
источник
+1 за пьюца. Это довольно быстро (3 секунды для сортировки 28000 слов), представляет собой чистый питон и не требует зависимости.
michaelmeyer
7

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

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

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

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

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

Леннарт Регебро
источник
6

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

В Python 3 этап «перестройки» можно сделать проще, например:

spec_dict = ''.maketrans(spec_dict)

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

Алекс Мартелли
источник
Этот метод хорош, но не позволяет поместить á между az и b
Барни
1

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

Брайан Сазерленд
источник
1

Полное решение УЦА

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

Христос
источник
2
Зачем вам вызывать Perl-скрипт, чтобы делать то, для чего существуют библиотеки Python?
Леннарт Регебро
2
Потому что я не знаю, была библиотека Python, вот почему!
tchrist
@Lennart: Я действительно предпочитаю нативные библиотеки, или в большинстве своем связанные с C API и динамически загружаемые (что иногда вам нужно). Я не нашел различные решения PyPerl и Inline :: Perl очень убедительными, надежными или гибкими. Или что-то. По некоторым причинам они просто не чувствуют себя подходящими. В последний раз я пробовал это, когда мне нужно было хорошее определение кодировки (которого, увы, у меня так и не было).
tchrist
4
Использование Perl внутри Python - это просто зависимость.
Утку Зихниоглу
1
Вот это да. Да - мне кажется Perl, на самом деле мы видим, что теперь есть более двух способов сделать что-то :) Ужасно трудно увидеть, что так нужно делать.
nealmcb 01
0

Это далеко от полного решения для Вашего случая использования, но вы могли бы взглянуть на unaccent.py сценарий из effbot.org. По сути, он удаляет все акценты из текста. Вы можете использовать этот «очищенный» текст для сортировки по алфавиту. (Более подробное описание см. На этой странице.)

Марк ван Лент
источник