Як сортувати рядки Unicode в алфавітному порядку в Python?


97

Python сортує за байтом значення за байтом, а це означає, що е після z та інших не менш кумедних речей. Який найкращий спосіб сортувати за алфавітом у Python?

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

Якщо для нього немає бібліотеки, який найкращий спосіб зробити це? Просто зробіть зіставлення з букви на ціле значення і з цим переведіть рядок на цілий список?


11
Зауважте, що це залежить навіть від локальності: у шведській мові (як ви заявляєте) "Ä" надходить після "Z", але німецькою мовою "Ä" зазвичай сортується як "AE".
balpha

@Georg: Чи була причина, що ти відкрив для цього суму? locale.strcollВідповідь є правильним , коли вам потрібно Unicode сортування з використанням локалі користувача, і ICU відповідь , що ви хочете , коли вам потрібно більше , ніж (звірку з використанням більш одного мовного стандарту). Більшу частину часу ти хочеш locale.strcoll.
Гленн Мейнард

@Glenn: Я хотів дізнатися, наскільки добре locale.strcollпрацює, і особливо те, що ICU робить краще, ніж функція Python. В основному трохи більше уваги до питання.
Георг Шеллі

1
@Georg: Останнім часом я багато грав з Алгоритмом зібрання Unicode, як ви бачите з моєї відповіді. Це справді чудово вміти, наприклад, сортувати, --locale=de__phonebookколи це потрібно. Модуль Perl передає тестовий набір UCA, а сценарій, який я надав, значно полегшує гру з усім UCA, а також усі його параметри, включаючи локалі, лише з командного рядка. Може не відповісти на питання, але це все одно має бути дуже цікавим. Якщо ви перебуваєте у Швейцарії, я впевнений, що ви могли б використовувати цю гнучкість. :)
tchrist

Відповіді:


75

ICU- бібліотека IBM робить це (і багато іншого). Він має зв'язки 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 2 та Python 3? Я використовував locale.strxfrmвідповідь у0b34a0f6ae, і, здається, працює і набагато елегантніше і не потребує додаткового програмного забезпечення.
суп

Не працює з Python3 для мене, sudo pip3 install PyICUне вдається встановити і так само для Python2.
імрек

Мені довелося встановити libicu-devel.x86_64 для pyICU для компіляції та встановлення з Pip. Він працює, хоча вихід із останньої команди «відсортований»: ['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)

Запитання до Леннарта та інших відповідачів: Хтось не знає "локал" чи це не вирішує це завдання?


До речі, 1) Я не думаю, що locale.strxfrm порушений для кодування UTF-8 `str '; Я встановив
орієнтацію

6
До речі 2) Модуль локалізації працюватиме лише з створеними локалями (для вікна Linux), а не з будь-яким довільним мовою. "locale -a" скаже вам, що
u0b34a0f6ae

6
@Georg: Я вважаю, що локаль підтримує лише просте відображення підрядків-> співставлення_елементів. Він не обробляє такі речі, як розширення (æ відсортовано як "ае"), французьке сортування наголосів (букви відсортовані зліва направо, але наголоси справа наліво), перестановка та, мабуть, ще кілька. Деталі тут (повний набір функцій UCA): unicode.org/reports/tr10 та тут (співставлення місцевості): chm.tu-dresden.de/edv/manuals/aix/files/aixfiles/LC_COLLATE.htm
Rafał Dowgird

2
Для того, щоб чітко відповісти на питання: Так, це до завдання. Мабуть, є деякі особливі випадки, коли повний алгоритм зібрання Unicode обробляється краще, але якщо ви вже не знали, що шанси ви не помітите.
Леннарт Регебро

1
Найбільша проблема тут полягає в тому, що вам потрібно встановити локальну програму для всього додатка. - Ви не можете просто мати це для порівняння.
Роберт Сімер

9

Спробуйте алгоритм зібрання унікоду Python Unicode Джеймса Таубера . Це може не робити саме так, як ви хочете, але здається, що варто переглянути. Для отримання додаткової інформації про проблеми дивіться цей пост Крістофера Ленца.


Це хоча б фіксує загальну проблему. Я думаю, що також можна створити чутливі до мови версії списку зіставлення.
Леннарт Регебро

Це не дозволяє вказати локаль, а посилальний конфігураційний файл викликає ValueError.
thebjorn

8

Можливо, вас також зацікавить pyuca :

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

Хоча це, безумовно, не найточніший спосіб, але це дуже простий спосіб принаймні дещо правильно. Він також перемагає локаль у веб-програмі, оскільки мова не є безпечною для потоків і встановлює мовні налаштування в усьому процесі. Це також простіше налаштувати, ніж PyICU, який спирається на зовнішню бібліотеку C.

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

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

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


+1 для pyuca. Він досить швидкий (3 секунди для сортування 28000 слів), чистий пітон і не потребує залежності.
michaelmeyer

7

Коротка інформація та розширена відповідь:

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

ICUЦе не обов'язково робити це краще на практиці, але проте робить і більше . Найбільше, що він має підтримку розгалужувачів, які можуть розділяти тексти на різних мовах на слова. Це дуже корисно для мов, у яких немає розділювачів слів. Потрібно мати корпус слів, який можна використовувати як основу для розщеплення, оскільки це не входить.

Він також має довгі імена для локалів, тому ви можете отримати досить відображувані імена для локалі, підтримку інших календарів, ніж григоріанський (хоча я не впевнений, що інтерфейс Python підтримує це) та тонни і тонни інших більш-менш незрозумілих локальних підтримок .

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


6

Я бачу, що відповіді вже виконали чудову роботу, просто хотілося вказати на неефективність кодування в Human Sort . Щоб застосувати селективний переклад char-by-char до рядка unicode 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 для видалення відповідного символу як частини перекладу, тому легко вказати "ignore a певний символ для сортування "," карта ä до ae для сортування "тощо).

У Python 3 ви можете отримати крок "перебудови" простіше, наприклад:

spec_dict = ''.maketrans(spec_dict)

Дивіться документи про інші способи використання цього maketransстатичного методу в Python 3.


Цей спосіб є приємним, але не дозволяє розмістити між az та b
Barney


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

Повне рішення 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 --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: Я справді віддаю перевагу власним бібліотекам або, максимум, тим, що пов'язані з API API та динамічно завантажуються (що вам іноді потрібно). Я не знайшов різні рішення PyPerl та Inline :: Perl дуже переконливими, надійними чи гнучкими. Або щось. Вони просто не відчувають себе з якихось причин. Я востаннє спробував це, коли мені було потрібне гарне виявлення гарсети (на жаль, я ніколи не отримував).
tchrist

4
Використання Perl всередині Python - це лише залежність.
Utku Zihnioglu

1
Ого. Так - мені схоже на Perl, адже ми бачимо, що зараз існує більше ніж два способи зробити :) Але виклик C з Python взагалі не означає типових додаткових залежностей та проблем практичної підтримки, які викликають Perl, тому його жахливо важко бачити багато закликів зробити це таким чином.
nealmcb

0

Це далеко від повного рішення для Вашого випадку використання, але ви могли б поглянути на unaccent.py сценарій з effbot.org. В основному, це видалити всі наголоси з тексту. Ви можете використовувати цей «очищений» текст для сортування за алфавітом. (Для кращого опису дивіться цю сторінку.)


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.