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

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

Est-il une bibliothèque? Je ne pouvais pas trouver quoi que ce soit. Préférablement le tri devrait avoir le soutien de langue de sorte qu'il comprenne que åäö devrait être trié après z en suédois, mais que ü devrait être trié par u, etc. Le support Unicode est donc à peu près une exigence.

S'il n'y a pas bibliothèque pour elle, ce qui est la meilleure façon de le faire? Il suffit de faire un mapping de la lettre à une valeur entière et de mapper la chaîne à une liste entière avec ça?

88
demandé sur Georg Schölly 2009-07-08 16:59:09

11 réponses

IBM ICU bibliothèque fait cela (et beaucoup plus). Il a des fixations Python: PyICU .

Update : la différence essentielle dans le tri entre ICU et locale.strcoll est que ICU utilise le plein algorithme de Collation Unicode tandis que strcoll utilise ISO 14651 .

Les différences entre ces deux algorithmes sont brièvement résumé ici: http://unicode.org/faq/collation.html#13 . Il s'agit de cas spéciaux assez 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']
66
répondu Rafał Dowgird 2014-10-22 11:04:40

Je ne vois pas cela dans les réponses. Mon Application trie selon la localisation en utilisant la bibliothèque standard de python. Il est assez facile.

# 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 de Lennart et d'autres answerers: plus personne Ne sait "locale" ou n'est-il pas jusqu'à cette tâche?

48
répondu u0b34a0f6ae 2009-08-23 14:41:14

Try James Tauber's Python Unicode Collation Algorithm . Il se peut qu'il ne fasse pas exactement ce que vous voulez, mais semble bien vaut un coup d'oeil. Pour un peu plus d'informations sur les questions, voir ce post par Christopher Lenz.

9
répondu Vinay Sajip 2009-07-08 13:08:24

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 la façon la plus exacte, c'est un moyen très simple pour au moins l'obtenir un peu juste. Il bat également locale dans une webapp comme locale n'est pas threadsafe et définit les paramètres de langue pour l'ensemble du processus. Il est également plus facile à mettre en place que PyICU qui repose sur une bibliothèque externe C.

j'ai téléchargé le script de github que l'original était au moment d'écrire ces lignes et j'ai dû recourir à des caches web:

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

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

8
répondu href_ 2011-12-16 13:54:45

Un résumé et étendu réponse:

locale.strcoll sous Python 2, et locale.strxfrm va en fait résoudre le problème, et fait un bon travail, en supposant que vous avez la locale en question installé. Je l'ai testé sous Windows aussi, où les noms de locales sont différents de façon confusante, mais d'un autre côté il semble que toutes les locales qui sont supportées soient installées par défaut.

ICU ne fait pas nécessairement mieux dans la pratique, il cependant, ne façon plus . Plus particulièrement, il a le soutien pour les splitters 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 de mots. Vous aurez besoin d'avoir un corpus de mots à utiliser comme une base pour la séparation, parce que ce n'est pas inclus.

il a également de longs noms pour les locales de sorte que vous pouvez obtenir de jolis noms d'affichage pour la locale, le soutien pour d'autres calendriers que grégorien (bien que Je ne suis pas sûr que L'interface Python supporte cela) et des tonnes et des tonnes d'autres supports plus ou moins obscurs.

donc tout en tous: si vous voulez trier alphabétiquement et dépendant de la locale, vous pouvez utiliser le module locale , sauf si vous avez des exigences spéciales, ou aussi besoin de plus de fonctionnalité dépendante de la locale, comme mots splitter.

7
répondu Lennart Regebro 2011-02-17 07:16:48
"151960920 je vois que les réponses ont déjà fait un excellent travail, je voulais juste insister sur un codage de l'inefficacité dans l' Humain . Pour appliquer une traduction sélective de char-by-char à une chaîne de caractères unicode, 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 façon 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 byte 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 les ordinaux Unicode (pas les chaînes) comme clés, c'est pourquoi nous avons besoin de cette étape de reconstruction à partir du char-to-char original spec_dict . (Les valeurs dans le dict que vous passez pour traduire [par opposition aux clés, qui doivent être des ordinales] peuvent être des ordinales 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 certain caractère pour le tri fins", "carte ä à ae à des fins de tri", etc).

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

spec_dict = ''.maketrans(spec_dict)

Voir les docs pour d'autres façons que vous pouvez utiliser ce maketrans méthode statique en Python 3.

6
répondu Alex Martelli 2009-07-08 14:57:16

d'Un UCA Solution

la façon la plus simple, la plus facile et la plus directe de le faire il pour faire un appel au module de bibliothèque Perl, Unicode::Collate::Locale , qui est une sous-classe de la norme Unicode::Collate module. Tout ce que vous devez faire est de passer au constructeur une valeur locale de "xv" pour la Suède.

(vous ne pouvez pas nécessairement apprécier ceci pour le texte Suédois), mais parce que Perl utilise des caractères abstraits, vous pouvez utiliser n'importe quel point de code Unicode s'il Vous Plaît - peu importe la plate-forme ou la construction! Peu de langues offrent une telle commodité. Je le mentionne parce que je me bats avec Java beaucoup pour 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 - apart, c'est-à-dire en utilisant un callout shell ou un tube recto verso. A 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é avec une facilité parfaite.

ce script est 100% compatible avec le algorithme de Collation Unicode , avec toutes les options de personnalisation prises en charge!! Et si vous avez un module optionnel installé ou lancé Perl 5.13 ou mieux, alors vous avez un accès complet à des locales CLDR faciles à utiliser. Voir ci-dessous.

Démonstration

Imaginez un jeu d'entrées commandé 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 par le livre de tout le monde. En utilisant mon script, qui utilise L'algorithme de Collation 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

C'est le tri UCA par défaut. Pour obtenir le lieu 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 de saisie. Tout d'abord, le jeu 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 code point, qui 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 en utilisant L'UCA par défaut le fait 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 le local Suédois, par ici:

% 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 en majuscules avant en minuscules, faites ceci:

% 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

Personnalisé Sortes

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 locale, vous devez installer le module CPAN optionnel Unicode::Collate::Locale . Alternativement, vous pouvez installer une version de développement de Perl, 5.13+, qui comprennent ce module standardly.

Conventions D'Appel

c'est un prototype rapide, donc ucsort est principalement sous(der)documenté. Mais C'est son SYNOPSIS de ce qui switches/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 trouver comment appeler les modules de bibliothèque Perl depuis Python directement sans appeler de script Perl, faites-le par tous les moyens. Je ne sais pas comment faire moi-même. J'aimerais savoir comment.

en attendant, je crois que ce script fera ce que vous devez faire dans tout son particulier - et plus! Je l'utilise maintenant pour tout le tri de texte. Il enfin fait ce dont j'ai besoin depuis très, très longtemps.

le seul inconvénient est que l'argument --locale fait descendre les performances dans les tubes, bien qu'il soit assez rapide pour un tri régulier, non local mais toujours conforme à 100% UCA tri. Comme il charge tout en mémoire, vous ne voulez probablement pas utiliser cela sur des documents gigaoctets. Je l'utilise plusieurs fois par jour, et il est sûr qu'il est grand d'avoir le tri de texte sain enfin.

2
répondu tchrist 2016-05-25 12:56:41

pour l'implémenter, vous aurez besoin de lire à propos de " Unicode collation algorithm" 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
répondu Anurag Uniyal 2009-07-08 13:13:38

dernièrement, j'ai utilisé zope.ucol ( ) https://pypi.python.org/pypi/zope.ucol ) pour cette tâche. Par exemple, le tri de l'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.l'ucol enveloppe aussi L'Unité de soins intensifs, donc ce serait une alternative à la PyICU.

1
répondu Brian Sutherland 2013-05-10 11:08:14

Jeff Atwood a écrit un bon post sur ordre de tri naturel , dans lequel il a lié à un script qui fait à peu près ce que vous demandez .

ce n'est pas un scénario banal, mais ça fait l'affaire.

0
répondu Simon Scarfe 2009-07-08 13:11:58

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

0
répondu Mark van Lent 2011-01-05 13:17:19