2009-12-02 17 views
48

Perl und einige andere aktuelle Regex-Engines unterstützen Unicode-Eigenschaften wie die Kategorie in einer Regex. Z.B. In Perl können Sie \p{Ll} verwenden, um einen beliebigen Kleinbuchstaben zu finden, oder p{Zs} für jedes Leerzeichen. Ich sehe keine Unterstützung dafür in den 2.x- oder 3.x-Zeilen von Python (mit Bedauern). Kennt jemand eine gute Strategie, um einen ähnlichen Effekt zu erzielen? Selbstgewählte Lösungen sind willkommen.Python-Regex entspricht Unicode-Eigenschaften

+10

Tatsächlich unterstützt Perl ** alle ** Unicode-Eigenschaften, nicht nur die allgemeinen Kategorien. Beispiele sind '\ p {Block = Griechisch}, \ p {Skript = Armenisch}, \ p {Allgemein_Kategorie = Großbuchstaben_Letter}, \ p {Weißer_Raum}, \ p {Alphabetisch}, \ p {Mathematik}, \ p {Bidi_Class = Right_to_Left}, \ p {Word_Break = A_Letter }, \ n {Numeric_Value = 10}, \ p {Hangul_Syllable_Type = Leading_Jamo}, \ p {Sentence_Break = SContinue}, und rund 1.000 weitere. Nur die Regexes von Perl und ICU kümmern sich um die vollständige Ergänzung der Unicode-Eigenschaften. Alle anderen decken ein paar wenige ab, normalerweise nicht einmal genug für minimale Unicode-Arbeit. – tchrist

Antwort

22

Haben Sie versucht Ponyguruma, eine Python-Bindung an die Oniguruma Engine für reguläre Ausdrücke? In dieser Engine können Sie einfach \p{Armenian} sagen, um armenischen Zeichen zu entsprechen. \p{Ll} oder \p{Zs} funktionieren auch.

+2

Dieses Modul hat nicht die gleiche API wie Python re Modul –

+7

Last Commit zu Ponyguruma Modul war offenbar 2010 (http://dev.pocoo.org/hg/sandbox/ponyguruma) während das Python-Regex-Modul auf PyPI aktiv entwickelt wird: http://pypi.python.org/pypi/regex – RichVel

4

Sie haben Recht, dass Unicode-Eigenschaftsklassen vom Python-Regex-Parser nicht unterstützt werden.

Wenn Sie einen netten Hack machen wollten, wäre das in der Regel nützlich, könnten Sie einen Präprozessor erstellen, der eine Zeichenfolge für solche Klassen-Token (\p{M} oder was auch immer) scannt und sie durch die entsprechenden Zeichensätze ersetzt Beispiel: \p{M} würde [\u0300–\u036F\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F] werden und \P{M} würde [^\u0300–\u036F\u1DC0–\u1DFF\u20D0–\u20FF\uFE20–\uFE2F] werden.

Menschen würden Ihnen danken. :)

+1

Richtig, das Erstellen von Charakterklassen kam mir in den Sinn. Aber mit ungefähr 40 Kategorien produziert man 80 Klassen, und das schließt Unicode-Skripte, Blöcke, Flugzeuge und so weiter nicht mit ein.Könnte ein kleines Open-Source-Projekt wert sein, aber immer noch ein Wartungs-Albtraum. Ich habe gerade festgestellt, dass re.VERBOSE nicht für Zeichenklassen gilt, also keine Kommentare hier oder Leerraum zur besseren Lesbarkeit ... – ThomasH

2

Beachten Sie, dass \p{Ll} keine Entsprechung in regulären Python-Ausdrücken hat, \p{Zs} sollte von '(?u)\s' abgedeckt werden. Die (?u), wie die Dokumentation sagt, "machen \ w, \ W, \ b, \ B, \ d, \ D, \ s und \ S abhängig von der Unicode-Eigenschaft Eigenschaften-Datenbank." Und \s bedeutet jedes Leerzeichen.

+0

Sie haben Recht. Problem ist, '(? U) \ s' ist größer als '\ p {Zs}', einschließlich z.B. Neue Zeile. Wenn Sie also wirklich nur Leerzeichenseparatoren abgleichen wollen, wird ersteres übergenerierend. – ThomasH

+3

@ThomasH: Um "Leerzeichen außer Zeilenumbruch" zu erhalten, kann man die doppelt negierte Zeichenklasse verwenden: '(? U) [^ \ S \ n]' – bukzor

6

Sie können sorgfältig auf jedes Zeichen verwenden unicodedata:

import unicodedata 

def strip_accents(x): 
    return u''.join(c for c in unicodedata.normalize('NFD', x) if unicodedata.category(c) != 'Mn') 
+0

Danke. Obwohl dies außerhalb von Regexs möglich ist, könnte dies in bestimmten Fällen eine brauchbare Alternative sein. – ThomasH

+0

Es scheint, dass das Modul "Unicodedata" von Python derzeit keine Informationen über z.B. das Skript oder den Unicode-Block eines Zeichens. Siehe auch https://stackoverflow.com/questions/48058402/unicode-table-information-about-a-character-in-python/48060112#48060112 – tripleee

52

Das regex Modul (eine Alternative zu dem Standard-Modul re) unterstützt Unicode-Codepoint-Eigenschaften mit der \p{} Syntax.

+1

Nicht sicher, wie vollständig die '' p {} '' Unterstützung ist, aber dieses Modul ist aktiv entwickelt und sollte schließlich das eingebaute '' re''-Modul ersetzen: siehe http://pypi.python.org/pypi/regex – RichVel

+4

+1: 'regex' ist ein Drop-In-Ersatz für stdlibs 're' Modul. Wenn Sie wissen, wie man "re" benutzt; Sie können sofort 'regex' verwenden. 'import regex as re' und Sie haben' \ p {} 'Syntax-Unterstützung. Hier ist ein [Beispiel, wie alle Interpunktionen in einem String mit '\ p {P}'] entfernt werden können (http://stackoverflow.com/a/11066687) – jfs

5

von homegrown Lösungen Sprechen, vor einiger Zeit schrieb ich eine kleine program, genau das zu tun - eine Unicode-Kategorie wie \p{...} in einem Bereich von Werten, aus dem Unicode-specification (v.5.0.0) extrahiert geschrieben konvertieren. Es werden nur Kategorien unterstützt (z. B. L, Zs) und sind auf den BMP beschränkt. Ich poste es hier für den Fall, dass jemand es nützlich findet (obwohl das Oniguruma wirklich eine bessere Option scheint).

Beispiel Nutzung:

>>> from unicode_hack import regex 
>>> pattern = regex(r'^\\p{Lu}(\\p{L}|\\p{N}|_)*') 
>>> print pattern.match(u'疂_1+2').group(0) 
疂_1 
>>> 

Hier ist die source. Es gibt auch eine JavaScript version, die die gleichen Daten verwendet.

+1

Schön, obwohl Sie handgefertigte Literale für die Bereiche verwenden im Code. Es wäre schön, wenn diese Literale aus einer Textform der Spezifikation erzeugt würden. Oder von unicodededata (http://docs.python.org/library/unicodedata.html#module-unicodedata). Sie könnten wahrscheinlich alle gültigen Unicode-Codepunkte durchlaufen und sie durch unicodedata.category() laufen lassen, und die Ausgabe verwenden, um die Karte zu füllen ... – ThomasH

+0

Danke für den Tipp, ich kann das irgendwann implementieren. Der obige Code wurde zuerst für JavaScript erstellt (zu dem es damals nur wenige sinnvolle Alternativen gab) und dann nach Python portiert. Ich habe ein paar Regexes über die Spezifikationen gemacht und mit einem Wegwerf-Skript abgeschlossen, aber ich stimme zu, dass ein wiederholbares Verfahren besser gewesen wäre, also kann ich es auf dem neuesten Stand halten. – mgibsonbr

+0

Ich habe eine schnelle Funktion gehackt, die die Karte dynamisch baut (mit nur Listen von Zeichen als Werte): def unicats (maxu): m = defaultdict (Liste) für i in Reichweite (maxu): versuchen : cat = unicodedata.category (unichr (i)) Ausnahme: cat = Keine wenn Katze: m [cat] .append (i) return m m = UniCats (10FFFF) beachten Sie, dass einige Kategorien wirklich groß werden (zB len (m ['Cn']) == 873882). – ThomasH