web-dev-qa-db-fra.com

Comment trier les chaînes unicode par ordre alphabétique en Python?

Python trie par valeur d'octet par défaut, ce qui signifie que é vient après z et d'autres choses tout aussi amusantes. Quelle est la meilleure façon de trier par ordre alphabétique en Python?

Existe-t-il une bibliothèque pour cela? Je n'ai rien trouvé. De préférence, le tri devrait avoir une prise en charge linguistique afin qu'il comprenne que åäö devrait être trié après z en suédois, mais que ü devrait être trié par u, etc. La prise en charge Unicode est donc à peu près une exigence.

S'il n'y a pas de bibliothèque pour cela, quelle est la meilleure façon de procéder? Faites simplement un mappage de la lettre à une valeur entière et mappez la chaîne à une liste entière avec ça?

94
Lennart Regebro

La bibliothèque ICU d'IBM fait cela (et beaucoup plus). Il a Python: PyIC .

Mise à jour : la principale différence de tri entre ICU et locale.strcoll signifie que ICU utilise l'intégralité de nicode Collation Algorithm tandis que strcoll utilise ISO 14651 .

Les différences entre ces deux algorithmes sont brièvement résumées ici: http://unicode.org/faq/collation.html#1 . Ce sont des cas spéciaux plutôt exotiques, qui devraient rarement compter dans la pratique.

>>> 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']
70
Rafał Dowgird

Je ne vois pas cela dans les réponses. Mon application trie en fonction des paramètres régionaux à l'aide de la bibliothèque standard de python. C'est assez simple.

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

Question à Lennart et à d'autres répondeurs: Est-ce que personne ne connaît les "paramètres régionaux" ou n'est-ce pas à la hauteur de cette tâche?

52
u0b34a0f6ae

Essayez James Tauber's Python Unicode Collation Algorithm . Il ne fait peut-être pas exactement ce que vous voulez, mais il vaut bien le coup d'œil. Pour un peu plus d'informations sur les problèmes, voir cet article par Christopher Lenz.

9
Vinay Sajip

Vous pourriez également être intéressé par pyuca :

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

Bien que ce ne soit certainement pas le moyen le plus exact, c'est un moyen très simple au moins de bien faire les choses. Il bat également les paramètres régionaux dans une webapp car les paramètres régionaux ne sont pas threadsafe et définissent les paramètres de langue à l'échelle du processus. Il est également plus facile à configurer que PyICU qui s'appuie sur une bibliothèque C externe.

J'ai téléchargé le script sur github car l'original était en panne au moment de la rédaction de cet article et j'ai dû recourir à des caches Web pour l'obtenir:

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

J'ai utilisé avec succès ce script pour trier sainement le texte allemand/français/italien dans un module plone.

8
href_

Une réponse résumée et étendue:

locale.strcoll sous Python 2 et locale.strxfrm résoudra le problème et fera du bon travail, en supposant que les paramètres régionaux en question soient installés. Je l'ai également testé sous Windows, où les noms de paramètres régionaux sont différents, mais d'un autre côté, il semble que tous les paramètres régionaux pris en charge soient installés par défaut.

ICU ne fait pas nécessairement mieux dans la pratique, mais en fait plus . Plus particulièrement, il prend en charge les séparateurs qui peuvent diviser des textes dans différentes langues en mots. Ceci est très utile pour les langues qui n'ont pas de séparateurs Word. Vous aurez besoin d'avoir un corpus de mots à utiliser comme base pour le fractionnement, car cela n'est pas inclus, cependant.

Il a également des noms longs pour les paramètres régionaux afin que vous puissiez obtenir de jolis noms d'affichage pour les paramètres régionaux, la prise en charge d'autres calendriers que grégorien (bien que je ne suis pas sûr que Python le supporte) et des tonnes et des tonnes d'autres supports régionaux plus ou moins obscurs.

Donc, dans l'ensemble: Si vous souhaitez trier par ordre alphabétique et dépendant des paramètres régionaux, vous pouvez utiliser le module locale, sauf si vous avez des exigences particulières, ou avez également besoin de plus de fonctionnalités dépendantes des paramètres régionaux, comme séparateur de mots.

7
Lennart Regebro

Je vois que les réponses ont déjà fait un excellent travail, je voulais juste souligner une inefficacité du codage dans Human Sort . Pour appliquer une traduction sélective caractère par caractère à une chaîne unicode s, il utilise le code:

spec_dict = {'Å':'A', 'Ä':'A'}

def spec_order(s):
    return ''.join([spec_dict.get(ch, ch) for ch in s])

Python a une manière bien meilleure, plus rapide et plus concise d'effectuer cette tâche auxiliaire (sur les chaînes Unicode - la méthode analogue pour les chaînes d'octets a une spécification différente et un peu moins utile! -):

spec_dict = dict((ord(k), spec_dict[k]) for k in spec_dict)

def spec_order(s):
    return s.translate(spec_dict)

Le dict que vous passez à la méthode translate a des ordinaux Unicode (pas des chaînes) comme clés, c'est pourquoi nous avons besoin de cette étape de reconstruction à partir du caractère d'origine en caractère spec_dict. (Les valeurs dans le dict que vous transmettez pour traduire [par opposition aux clés, qui doivent être des ordinaux] peuvent être des ordinaux Unicode, des chaînes Unicode arbitraires ou Aucune pour supprimer le caractère correspondant dans le cadre de la traduction, il est donc facile de spécifier "ignorer un certains caractères à des fins de tri "," mapper ä à ae à des fins de tri ", etc.).

Dans Python 3, vous pouvez obtenir l'étape de "reconstruction" plus simplement, par exemple:

spec_dict = ''.maketrans(spec_dict)

Voir la documentation pour d'autres façons d'utiliser cette méthode statique maketrans dans Python 3.

6
Alex Martelli

Une solution UCA complète

La façon la plus simple, la plus simple et la plus directe de le faire consiste à appeler le module de bibliothèque Perl, Unicode :: Collate :: Locale , qui est une sous-classe de la norme Module Unicode :: Collate . Il vous suffit de transmettre au constructeur une valeur locale de "xv" pour la Suède.

(Vous pouvez ne pas l'apprécier nécessairement pour le texte suédois, mais parce que Perl utilise des caractères abstraits, vous pouvez utiliser n'importe quel point de code Unicode qui vous plaît - peu importe la plate-forme ou la construction! Peu de langues offrent une telle commodité. Je le mentionne parce que j'ai combattu un perdre la bataille avec Java beaucoup sur ce problème exaspérant ces derniers temps.)

Le problème est que je ne sais pas comment accéder à un module Perl à partir de Python - à part, c'est-à-dire à l'aide d'une légende Shell ou d'un canal bilatéral. À cette fin, Je vous ai donc fourni un script de travail complet appelé ucsort que vous pouvez appeler pour faire exactement ce que vous avez demandé en toute simplicité.

Ce script est 100% conforme à l'algorithme complet de classement Unicode, avec toutes les options de personnalisation prises en charge !! Et si vous avez un module optionnel installé ou exécutez Perl 5.13 ou mieux, alors vous avez un accès complet aux paramètres régionaux CLDR faciles à utiliser. Voir ci-dessous.

Manifestation

Imaginez un ensemble d'entrées ordonné de cette façon:

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

Un tri par défaut par point de code donne:

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 ä å ö

ce qui est incorrect dans le livre de tout le monde. En utilisant mon script, qui utilise l'algorithme de classement Unicode, vous obtenez cet ordre:

% 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

Il s'agit du tri UCA par défaut. Pour obtenir les paramètres régionaux suédois, appelez ucsort de cette façon:

% 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 å ä ö

Voici une meilleure démo d'entrée. Tout d'abord, l'ensemble d'entrée:

% 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

Par point de code, cela trie de cette façon:

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

Mais l'utilisation de l'UCA par défaut permet de trier de cette façon:

% 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

Mais dans la langue suédoise, de cette façon:

% 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

Si vous préférez trier les majuscules avant les minuscules, procédez comme suit:

% 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

Tris personnalisés

Vous pouvez faire beaucoup d'autres choses avec ucsort . Par exemple, voici comment trier les titres en anglais:

% 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

Vous aurez besoin de Perl 5.10.1 ou mieux pour exécuter le script en général. Pour la prise en charge des paramètres régionaux, vous devez installer le module CPAN facultatif Unicode::Collate::Locale. Alternativement, vous pouvez installer une version de développement de Perl, 5.13+, qui inclut ce module en standard.

Conventions d'appel

Il s'agit d'un prototype rapide, donc ucsort est généralement non (der) documenté. Mais c'est son SYNOPSIS des commutateurs/options qu'il accepte sur la ligne de commande:

    # 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

Ouais, ok: c'est vraiment la liste d'arguments que j'utilise pour l'appel à Getopt::Long, mais vous avez l'idée. :)

Si vous pouvez comprendre comment appeler des modules de bibliothèque Perl à partir de Python directement sans appeler un script Perl, faites-le par tous les moyens. Je ne sais pas comment faire moi-même. J'aimerais apprendre Comment.

En attendant, je crois que ce script fera ce dont vous avez besoin dans tous ses détails - et plus! J'utilise maintenant cela pour tout le tri de texte. Il enfin fait ce dont j'ai besoin depuis très longtemps.

Le seul inconvénient est que --locale l'argument fait baisser les performances, bien qu'il soit suffisamment rapide pour un tri régulier, non local mais toujours 100% compatible UCA . Puisqu'il charge tout en mémoire, vous ne voudrez probablement pas l'utiliser sur des documents gigaoctets. Je l'utilise plusieurs fois par jour, et c'est sûr que c'est bien d'avoir enfin un tri sain du texte.

2
tchrist

Pour l'implémenter, vous devrez lire à propos de "l'algorithme de classement Unicode" voir http://en.wikipedia.org/wiki/Unicode_collation_algorithm

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

un exemple d'implémentation est ici

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

1
Anurag Uniyal

Dernièrement, j'utilise zope.ucol ( https://pypi.python.org/pypi/zope.ucol ) pour cette tâche. Par exemple, trier le ß allemand:

>>> 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 enveloppe également ICU, serait donc une alternative à PyICU.

1
Brian Sutherland

Jeff Atwood a écrit un bon article sur Ordre de tri naturel , il y a lié un script qui fait à peu près ce que vous demandez .

Ce n'est en aucun cas un script trivial, mais il fait l'affaire.

0
Simon Scarfe

C'est loin d'être une solution complète pour votre cas d'utilisation, mais vous pouvez jeter un œil au script naccent.py d'effbot.org. Ce qu'il fait essentiellement, c'est de supprimer tous les accents d'un texte. Vous pouvez utiliser ce texte "filtré" pour trier par ordre alphabétique. (Pour une meilleure description, voir this page.)

0
Mark van Lent