it-swarm.com.de

Wie sortiere ich Unicode-Zeichenfolgen in Python alphabetisch?

Python sortiert standardmäßig nach Bytewert, was bedeutet, dass é nach z und anderen ebenso lustigen Dingen kommt. Was ist der beste Weg, um in Python alphabetisch zu sortieren?

Gibt es dafür eine Bibliothek? Ich konnte nichts finden. Das Sortieren sollte vorzugsweise eine Sprachunterstützung haben, so dass klar ist, dass åäö auf Schwedisch nach z sortiert werden sollte, ü jedoch nach u usw. sortiert werden sollte. Die Unicode-Unterstützung ist daher ziemlich wichtig.

Was ist der beste Weg, dies zu tun, wenn es keine Bibliothek dafür gibt? Nehmen Sie einfach eine Zuordnung von Buchstaben zu einem ganzzahligen Wert vor und ordnen Sie die Zeichenfolge damit einer ganzzahligen Liste zu.

94
Lennart Regebro

Die IBM-Bibliothek ICU macht das (und vieles mehr). Es hat Python Bindungen: PyIC .

Update : Der Hauptunterschied bei der Sortierung zwischen ICU und locale.strcoll ist, dass ICU verwendet den vollständigen nicode-Kollatierungsalgorithmus , während strcollISO 14651 verwendet.

Die Unterschiede zwischen diesen beiden Algorithmen werden hier kurz zusammengefasst: http://unicode.org/faq/collation.html#1 . Dies sind eher exotische Sonderfälle, die in der Praxis selten eine Rolle spielen sollten.

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

Ich sehe das nicht in den Antworten. Meine Anwendung wird mit der Standardbibliothek von Python nach dem Gebietsschema sortiert. Das ist ziemlich einfach.

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

Frage an Lennart und andere Antwortende: Kennt jemand das Gebietsschema nicht oder liegt es nicht an dieser Aufgabe?

52
u0b34a0f6ae

Versuchen Sie es mit James Taubers Python Unicode Collation Algorithm . Möglicherweise macht es nicht genau das, was Sie wollen, aber es scheint einen Blick wert zu sein. Weitere Informationen zu den Problemen finden Sie in dieser Beitrag von Christopher Lenz.

9
Vinay Sajip

Das könnte Sie auch interessieren pyuca :

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

Es ist sicherlich nicht der genaueste Weg, aber es ist ein sehr einfacher Weg, es zumindest einigermaßen richtig zu machen. Es schlägt auch das Gebietsschema in einer Webanwendung, da das Gebietsschema nicht threadsicher ist und die Spracheinstellungen prozessweit festgelegt werden. Es ist auch einfacher einzurichten als PyICU, das auf einer externen C-Bibliothek beruht.

Ich habe das Skript auf github hochgeladen, da das Original zum Zeitpunkt des Schreibens nicht verfügbar war, und ich musste auf Web-Caches zurückgreifen, um es zu erhalten:

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

Ich habe dieses Skript erfolgreich verwendet, um deutschen/französischen/italienischen Text in einem Plone-Modul sicher zu sortieren.

8
href_

Eine Zusammenfassung und erweiterte Antwort:

locale.strcoll unter Python 2 und locale.strxfrm löst das Problem in der Tat und leistet gute Arbeit, vorausgesetzt, Sie haben das betreffende Gebietsschema installiert. Ich habe es auch unter Windows getestet, wo die Namen der Gebietsschemas verwirrend unterschiedlich sind, aber auf der anderen Seite scheinen alle unterstützten Gebietsschemas standardmäßig installiert zu sein.

ICU macht das in der Praxis nicht unbedingt besser, aber es macht viel mehr . Insbesondere werden Splitter unterstützt, die Texte in verschiedenen Sprachen in Wörter aufteilen können. Dies ist sehr nützlich für Sprachen ohne Word-Trennzeichen. Sie benötigen eine ganze Reihe von Wörtern, die Sie als Grundlage für die Aufteilung verwenden können, da dies jedoch nicht enthalten ist.

Es hat auch lange Namen für die Gebietsschemas, so dass Sie hübsche Anzeigenamen für das Gebietsschema erhalten können, Unterstützung für andere Kalender als Gregorianisch (obwohl ich nicht sicher bin, ob die Python Schnittstelle das unterstützt) und Tonnen und Tonnen von anderen mehr oder weniger obskuren lokalen Unterstützungen.

Alles in allem also: Wenn Sie alphabetisch und abhängig vom Gebietsschema sortieren möchten, können Sie das Modul locale verwenden, es sei denn, Sie haben spezielle Anforderungen, oder Sie benötigen mehr vom Gebietsschema abhängige Funktionen, z Wörter splitter.

7
Lennart Regebro

Ich sehe, die Antworten haben bereits hervorragende Arbeit geleistet, ich wollte nur auf eine Ineffizienz der Codierung in Human Sort hinweisen. Um eine selektive char-by-char-Übersetzung auf eine Unicode-Zeichenfolge anzuwenden, wird der folgende Code verwendet:

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

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

Python bietet eine viel bessere, schnellere und präzisere Möglichkeit, diese Hilfsaufgabe auszuführen (für Unicode-Strings - die analoge Methode für Byte-Strings hat eine andere und etwas weniger hilfreiche Spezifikation! -):

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

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

Das Diktat, das Sie an die translate -Methode übergeben, enthält Unicode-Ordnungszahlen (keine Zeichenfolgen) als Schlüssel. Aus diesem Grund benötigen wir diesen Neuerstellungsschritt vom ursprünglichen char-to-char spec_dict. (Werte in dem zu übersetzenden Diktat [im Gegensatz zu Schlüsseln, bei denen es sich um Ordnungszahlen handeln muss] können Unicode-Ordnungszahlen, beliebige Unicode-Zeichenfolgen oder Keine sein, um das entsprechende Zeichen als Teil der Übersetzung zu entfernen. Daher ist es einfach anzugeben, "a zu ignorieren" bestimmte Zeichen zu Sortierzwecken "," Zuordnung ä zu ae zu Sortierzwecken "und dergleichen).

In Python 3 können Sie den Schritt "Neuerstellen" einfacher ausführen, z. B .:

spec_dict = ''.maketrans(spec_dict)

In the docs finden Sie weitere Möglichkeiten, wie Sie diese statische Methode maketrans in Python 3 verwenden können.

6
Alex Martelli

Eine vollständige UCA-Lösung

Die einfachste, einfachste und direkteste Möglichkeit, dies zu tun, besteht darin, ein Callout für das Perl-Bibliotheksmodul nicode :: Collate :: Locale zu erstellen, das eine Unterklasse des Standards nicode :: Sortieren Modul. Sie müssen dem Konstruktor lediglich einen Gebietsschema-Wert von "xv" Für Schweden übergeben.

(Sie werden das vielleicht nicht unbedingt für schwedischen Text zu schätzen wissen, aber weil Perl abstrakte Zeichen verwendet, können Sie jeden beliebigen Unicode-Codepunkt verwenden - unabhängig von der Plattform oder dem Build! Nur wenige Sprachen bieten einen solchen Komfort den Kampf mit Java viel über dieses verrückte Problem in letzter Zeit verlieren.)

Das Problem ist, dass ich nicht weiß, wie ich von Python - abgesehen von der Verwendung eines Shell-Callouts oder einer zweiseitigen Pipe auf ein Perl-Modul zugreifen soll. Zu diesem Zweck kann I haben Ihnen daher ein vollständiges Arbeitsskript mit dem Namen ucsort zur Verfügung gestellt, mit dem Sie problemlos genau das tun können, wonach Sie gefragt haben.

Dieses Skript ist 100% kompatibel mit dem vollständigen nicode Collation Algorithm, wobei alle Anpassungsoptionen unterstützt werden !! Wenn Sie ein optionales Modul installiert haben oder Perl 5.13 oder höher ausführen, haben Sie vollen Zugriff auf benutzerfreundliche CLDR-Gebietsschemas. Siehe unten.

Demonstration

Stellen Sie sich einen Eingabesatz vor, der folgendermaßen angeordnet ist:

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

Eine Standard-Sortierung nach Codepunkt ergibt:

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

was nach jedermanns Buch falsch ist. Mit meinem Skript, das den Unicode-Kollatierungsalgorithmus verwendet, erhalten Sie folgende Reihenfolge:

% 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

Dies ist die Standard-UCA-Sortierung. Um das schwedische Gebietsschema zu erhalten, rufen Sie ucsort folgendermaßen auf:

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

Hier ist eine bessere Eingabedemo. Zunächst wird der Eingabesatz:

% 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

Nach Codepunkten sortiert dies folgendermaßen:

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

Bei Verwendung der Standard-UCA ist dies jedoch folgendermaßen zu sortieren:

% 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

Aber im schwedischen Gebietsschema:

% 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

Wenn Sie es vorziehen, Großbuchstaben vor Kleinbuchstaben zu sortieren, gehen Sie folgendermaßen vor:

% 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

Kundenspezifische Sortierungen

Sie können viele andere Dinge mit ucsort tun. So sortieren Sie beispielsweise Titel in Englisch:

% 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

Sie benötigen Perl 5.10.1 oder höher, um das Skript im Allgemeinen auszuführen. Für die Unterstützung des Gebietsschemas müssen Sie entweder das optionale CPAN-Modul Unicode::Collate::Locale Installieren. Alternativ können Sie eine Entwicklungsversion von Perl 5.13+ installieren, die dieses Modul standardmäßig enthält.

Aufruf von Konventionen

Dies ist ein schneller Prototyp, daher ist ucsort meist un (der) dokumentiert. Dies ist jedoch die ÜBERSICHT darüber, welche Schalter/Optionen in der Befehlszeile akzeptiert werden:

    # 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

Ja, ok, das ist wirklich die Argumentliste, die ich für den Aufruf von Getopt::Long Verwende, aber Sie haben die Idee. :)

Wenn Sie herausfinden können, wie Sie Perl-Bibliotheksmodule aus Python direkt aufrufen können, ohne ein Perl-Skript aufzurufen, tun Sie dies auf jeden Fall. Ich weiß einfach nicht, wie ich es selbst. Ich würde gerne lernen Wie.

In der Zwischenzeit glaube ich, dass dieses Skript genau das tut, was Sie tun müssen - und mehr! Ich verwende dies jetzt für die gesamte Textsortierung. Es endlich tut, was ich seit langer, langer Zeit brauche.

Der einzige Nachteil ist, dass das Argument --locale Die Leistung beeinträchtigt, obwohl es schnell genug für reguläres, nicht lokales , aber immer noch 100% UCA-konformes Sortieren ist . Da alles im Speicher geladen wird, möchten Sie dies wahrscheinlich nicht für Gigabyte-Dokumente verwenden. Ich benutze es oft am Tag und es ist sicher großartig, endlich vernünftigen Text zu sortieren.

2
tchrist

Um es zu implementieren, müssen Sie sich mit "Unicode-Kollatierungsalgorithmus" vertraut machen (siehe http://en.wikipedia.org/wiki/Unicode_collation_algorithm

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

eine Beispielimplementierung finden Sie hier

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

1
Anurag Uniyal

In letzter Zeit habe ich zope.ucol ( https://pypi.python.org/pypi/zope.ucol ) für diese Aufgabe verwendet. Zum Beispiel das deutsche ß sortieren:

>>> 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 wickelt auch die Intensivstation ein, wäre also eine Alternative zu PyICU.

1

Jeff Atwood schrieb einen guten Beitrag über Natural Sort Order , in dem er auf ein Skript verwies, das ziemlich genau das, was Sie fragen tut.

Es ist keineswegs ein triviales Skript, aber es macht den Trick.

0
Simon Scarfe

Es ist alles andere als eine vollständige Lösung für Ihren Anwendungsfall, aber Sie können sich das Skript naccent.py von effbot.org ansehen. Grundsätzlich werden alle Akzente aus einem Text entfernt. Sie können diesen bereinigten Text verwenden, um alphabetisch zu sortieren. (Für eine bessere Beschreibung siehe this page.)

0
Mark van Lent