Regular Expression HOWTO¶
- Autor:
A.M. Kuchling <amk@amk.ca>
Einleitung¶
Reguläre Ausdrücke (REs, Regexes oder Regex-Muster genannt) sind im Wesentlichen eine winzige, hochspezialisierte Programmiersprache, die in Python eingebettet und über das Modul re verfügbar ist. Mit dieser kleinen Sprache geben Sie die Regeln für die Menge möglicher Zeichenketten an, die Sie abgleichen möchten; diese Menge kann englische Sätze, E-Mail-Adressen, TeX-Befehle oder alles Mögliche enthalten. Sie können dann Fragen stellen wie: „Passt diese Zeichenkette zu dem Muster?“ oder „Gibt es irgendwo in dieser Zeichenkette eine Übereinstimmung mit dem Muster?“. Sie können REs auch verwenden, um eine Zeichenkette zu ändern oder sie auf verschiedene Arten aufzuteilen.
Reguläre Ausdrucksmuster werden in eine Reihe von Bytecodes kompiliert, die dann von einer Matching-Engine, die in C geschrieben ist, ausgeführt werden. Für fortgeschrittene Anwendungen kann es notwendig sein, genau darauf zu achten, wie die Engine einen bestimmten RE ausführt, und den RE so zu schreiben, dass Bytecodes erzeugt werden, die schneller laufen. Optimierung ist in diesem Dokument nicht abgedeckt, da sie ein gutes Verständnis der Interna der Matching-Engine erfordert.
Die Sprache der regulären Ausdrücke ist relativ klein und eingeschränkt, sodass nicht alle möglichen Zeichenverarbeitungsaufgaben mit regulären Ausdrücken erledigt werden können. Es gibt auch Aufgaben, die mit regulären Ausdrücken erledigt werden *können*, aber die Ausdrücke sind sehr kompliziert. In diesen Fällen ist es wahrscheinlich besser, Python-Code zur Verarbeitung zu schreiben; während Python-Code langsamer als ein aufwendiger regulärer Ausdruck ist, wird er wahrscheinlich auch verständlicher sein.
Einfache Muster¶
Wir beginnen mit den einfachsten möglichen regulären Ausdrücken. Da reguläre Ausdrücke zum Arbeiten mit Zeichenketten verwendet werden, beginnen wir mit der häufigsten Aufgabe: dem Abgleichen von Zeichen.
Für eine detaillierte Erklärung der Informatik, die regulären Ausdrücken zugrunde liegt (deterministische und nicht-deterministische endliche Automaten), können Sie sich fast jedes Lehrbuch zum Schreiben von Compilern ansehen.
Zeichen abgleichen¶
Die meisten Buchstaben und Zeichen stimmen mit sich selbst überein. Zum Beispiel gleicht der reguläre Ausdruck test genau der Zeichenkette test. (Sie können einen Modus ohne Berücksichtigung der Groß- und Kleinschreibung aktivieren, der es diesem RE ermöglicht, auch Test oder TEST abzugleichen; dazu später mehr.)
Es gibt Ausnahmen von dieser Regel; einige Zeichen sind spezielle Metazeichen und stimmen nicht mit sich selbst überein. Stattdessen signalisieren sie, dass etwas Ungewöhnliches abgeglichen werden soll, oder sie beeinflussen andere Teile des RE, indem sie diese wiederholen oder ihre Bedeutung ändern. Ein großer Teil dieses Dokuments widmet sich der Diskussion verschiedener Metazeichen und ihrer Bedeutung.
Hier ist eine vollständige Liste der Metazeichen; ihre Bedeutungen werden im Rest dieses HOWTOs besprochen.
. ^ $ * + ? { } [ ] \ | ( )
Die ersten Metazeichen, die wir betrachten, sind [ und ]. Sie werden zur Angabe einer Zeichenklasse verwendet, die eine Menge von Zeichen ist, die Sie abgleichen möchten. Zeichen können einzeln aufgelistet werden, oder ein Bereich von Zeichen kann durch Angabe zweier Zeichen und Trennung durch einen '-' angezeigt werden. Zum Beispiel gleicht [abc] eines der Zeichen a, b oder c ab; dies ist dasselbe wie [a-c], das einen Bereich verwendet, um dieselbe Menge von Zeichen auszudrücken. Wenn Sie nur Kleinbuchstaben abgleichen möchten, wäre Ihr RE [a-z].
Metazeichen (außer \) sind innerhalb von Klassen nicht aktiv. Zum Beispiel gleicht [akm$] eines der Zeichen 'a', 'k', 'm' oder '$' ab; '$' ist normalerweise ein Metazeichen, aber innerhalb einer Zeichenklasse verliert es seine spezielle Bedeutung.
Sie können die Zeichen, die nicht innerhalb der Klasse aufgeführt sind, durch Komplementierung der Menge abgleichen. Dies wird durch die Aufnahme eines '^' als erstes Zeichen der Klasse angezeigt. Zum Beispiel gleicht [^5] jedes Zeichen außer '5' ab. Wenn das Caret woanders in einer Zeichenklasse erscheint, hat es keine besondere Bedeutung. Zum Beispiel: [5^] gleicht entweder eine '5' oder ein '^' ab.
Das vielleicht wichtigste Metazeichen ist der Backslash, \. Wie bei Python-String-Literalen kann dem Backslash eine Vielzahl von Zeichen folgen, um verschiedene spezielle Sequenzen anzuzeigen. Er wird auch verwendet, um alle Metazeichen zu maskieren, damit Sie sie immer noch in Mustern abgleichen können; wenn Sie beispielsweise eine [ oder ein \ abgleichen müssen, können Sie ihnen einen Backslash voranstellen, um ihre besondere Bedeutung aufzuheben: \[ oder \\.
Einige der speziellen Sequenzen, die mit '\' beginnen, stellen vordefinierte Zeichensätze dar, die oft nützlich sind, wie z. B. die Menge der Ziffern, die Menge der Buchstaben oder die Menge von allem, was kein Leerraum ist.
Nehmen wir ein Beispiel: \w gleicht jedes alphanumerische Zeichen ab. Wenn das Regex-Muster in Bytes ausgedrückt wird, ist dies äquivalent zur Klasse [a-zA-Z0-9_]. Wenn das Regex-Muster ein String ist, gleicht \w alle Zeichen ab, die in der Unicode-Datenbank des Moduls unicodedata als Buchstaben markiert sind. Sie können die eingeschränktere Definition von \w in einem String-Muster verwenden, indem Sie das Flag re.ASCII beim Kompilieren des regulären Ausdrucks angeben.
Die folgende Liste spezieller Sequenzen ist nicht vollständig. Eine vollständige Liste der Sequenzen und erweiterte Klassendefinitionen für Unicode-String-Muster finden Sie im letzten Teil von Reguläre Ausdruckssyntax in der Standardbibliotheksreferenz. Im Allgemeinen stimmen die Unicode-Versionen mit jedem Zeichen überein, das sich in der entsprechenden Kategorie der Unicode-Datenbank befindet.
\dGleicht jede Dezimalziffer ab; dies ist äquivalent zur Klasse
[0-9].\DGleicht jedes Nicht-Ziffern-Zeichen ab; dies ist äquivalent zur Klasse
[^0-9].\sGleicht jedes Leerzeichen ab; dies ist äquivalent zur Klasse
[ \t\n\r\f\v].\SGleicht jedes Nicht-Leerzeichen ab; dies ist äquivalent zur Klasse
[^ \t\n\r\f\v].\wGleicht jedes alphanumerische Zeichen ab; dies ist äquivalent zur Klasse
[a-zA-Z0-9_].\WGleicht jedes nicht-alphanumerische Zeichen ab; dies ist äquivalent zur Klasse
[^a-zA-Z0-9_].
Diese Sequenzen können innerhalb einer Zeichenklasse enthalten sein. Zum Beispiel ist [\s,.] eine Zeichenklasse, die jedes Leerzeichen oder ',' oder '.' abgleicht.
Das letzte Metazeichen in diesem Abschnitt ist .. Es gleicht alles außer einem Zeilenumbruch ab, und es gibt einen alternativen Modus (re.DOTALL), bei dem es auch einen Zeilenumbruch abgleicht. . wird oft dort verwendet, wo Sie „beliebiges Zeichen“ abgleichen möchten.
Dinge wiederholen¶
Die Fähigkeit, verschiedene Zeichensätze abzugleichen, ist das Erste, was reguläre Ausdrücke können, was mit den auf Strings verfügbaren Methoden noch nicht möglich ist. Wenn dies jedoch die einzige zusätzliche Fähigkeit von Regexes wäre, wären sie kein großer Fortschritt. Eine weitere Fähigkeit ist, dass Sie angeben können, dass Teile des RE eine bestimmte Anzahl von Malen wiederholt werden müssen.
Das erste Metazeichen zum Wiederholen von Dingen, das wir uns ansehen, ist *. * stimmt nicht mit dem Literalzeichen '*' überein; stattdessen gibt es an, dass das vorherige Zeichen null- oder mehrmals, anstatt genau einmal, abgeglichen werden kann.
Zum Beispiel gleicht ca*t die Zeichenkette 'ct' (0 'a' Zeichen), 'cat' (1 'a'), 'caaat' (3 'a' Zeichen) und so weiter ab.
Wiederholungen wie * sind gierig; beim Wiederholen eines RE versucht die Matching-Engine, ihn so oft wie möglich zu wiederholen. Wenn spätere Teile des Musters nicht übereinstimmen, weicht die Matching-Engine zurück und versucht es erneut mit weniger Wiederholungen.
Ein schrittweises Beispiel macht dies deutlicher. Betrachten wir den Ausdruck a[bcd]*b. Dieser gleicht den Buchstaben 'a', null oder mehr Buchstaben aus der Klasse [bcd] und endet schließlich mit einem 'b'. Stellen Sie sich nun vor, diesen RE gegen die Zeichenkette 'abcbd' abzugleichen.
Schritt |
Abgeglichen |
Erläuterung |
|---|---|---|
1 |
|
Das |
2 |
|
Die Engine gleicht |
3 |
Fehler |
Die Engine versucht, |
4 |
|
Zurücksetzen, sodass |
5 |
Fehler |
Versuchen Sie |
6 |
|
Nochmal zurückgehen, sodass |
6 |
|
Versuchen Sie |
Das Ende des RE wurde nun erreicht, und es wurde 'abcb' abgeglichen. Dies zeigt, wie die Matching-Engine zuerst so weit wie möglich geht und, wenn keine Übereinstimmung gefunden wird, schrittweise zurückgeht und den Rest des RE immer wieder neu versucht. Sie wird zurückgehen, bis sie null Wiederholungen für [bcd]* versucht hat, und wenn das anschließend fehlschlägt, wird die Engine zu dem Schluss kommen, dass die Zeichenkette überhaupt nicht mit dem RE übereinstimmt.
Ein weiteres wiederholendes Metazeichen ist +, das ein- oder mehrmals abgeglichen wird. Achten Sie genau auf den Unterschied zwischen * und +; * gleicht *null* oder mehr Mal ab, daher muss das Wiederholte möglicherweise gar nicht vorhanden sein, während + mindestens *eine* Wiederholung erfordert. Um ein ähnliches Beispiel zu verwenden, gleicht ca+t die Zeichenkette 'cat' (1 'a'), 'caaat' (3 'a's) ab, aber nicht 'ct'.
Es gibt zwei weitere wiederholende Operatoren oder Quantifikatoren. Das Fragezeichen, ?, gleicht entweder einmal oder nullmal ab; Sie können es als Kennzeichnung von etwas als optional betrachten. Zum Beispiel gleicht home-?brew entweder 'homebrew' oder 'home-brew' ab.
Der komplizierteste Quantifizierer ist {m,n}, wobei m und n Dezimalzahlen sind. Dieser Quantifizierer bedeutet, dass es mindestens m Wiederholungen und höchstens n Wiederholungen geben muss. Zum Beispiel gleicht a/{1,3}b die Zeichenkette 'a/b', 'a//b' und 'a///b' ab. Sie stimmt nicht mit 'ab' überein, die keine Schrägstriche hat, oder 'a////b', die vier hat.
Sie können entweder m oder n weglassen; in diesem Fall wird ein vernünftiger Wert für den fehlenden Wert angenommen. Das Weglassen von m wird als untere Grenze von 0 interpretiert, während das Weglassen von n zu einer oberen Grenze von unendlich führt.
Der einfachste Fall {m} gleicht das vorhergehende Element genau m Mal ab. Zum Beispiel gleicht a/{2}b nur die Zeichenkette 'a//b' ab.
Leser mit reduktionistischem Einschlag mögen bemerken, dass die drei anderen Quantifizierer alle mit dieser Notation ausgedrückt werden können. {0,} ist dasselbe wie *, {1,} ist äquivalent zu +, und {0,1} ist dasselbe wie ?. Es ist besser, *, + oder ? zu verwenden, wenn Sie können, einfach weil sie kürzer und leichter zu lesen sind.
Reguläre Ausdrücke verwenden¶
Nachdem wir uns einige einfache reguläre Ausdrücke angesehen haben, wie verwenden wir sie tatsächlich in Python? Das Modul re bietet eine Schnittstelle zur Regex-Engine, die es Ihnen ermöglicht, REs in Objekte zu kompilieren und dann damit abzugleichen.
Reguläre Ausdrücke kompilieren¶
Reguläre Ausdrücke werden in Musterobjekte kompiliert, die Methoden für verschiedene Operationen haben, wie z. B. das Suchen nach Musterübereinstimmungen oder das Durchführen von String-Substitutionen.
>>> import re
>>> p = re.compile('ab*')
>>> p
re.compile('ab*')
re.compile() akzeptiert auch ein optionales Argument für *Flags*, das verwendet wird, um verschiedene spezielle Funktionen und Syntaxvariationen zu aktivieren. Wir werden die verfügbaren Einstellungen später besprechen, aber vorerst genügt ein Beispiel
>>> p = re.compile('ab*', re.IGNORECASE)
Der RE wird als String an re.compile() übergeben. REs werden als Strings behandelt, da reguläre Ausdrücke kein Teil der Kernsprache Python sind und keine spezielle Syntax zu ihrer Darstellung erstellt wurde. (Es gibt Anwendungen, die überhaupt keine REs benötigen, daher gibt es keinen Grund, die Sprachspezifikation durch deren Einbeziehung aufzublähen.) Stattdessen ist das Modul re einfach ein C-Erweiterungsmodul, das mit Python geliefert wird, genau wie die Module socket oder zlib.
Das Einbetten von REs in Strings hält die Python-Sprache einfacher, hat aber einen Nachteil, der das Thema des nächsten Abschnitts ist.
Die Backslash-Plage¶
Wie bereits erwähnt, verwenden reguläre Ausdrücke das Backslash-Zeichen ('\'), um spezielle Formen anzuzeigen oder spezielle Zeichen ohne deren Sonderbedeutung verwenden zu können. Dies steht im Konflikt mit Pythons Verwendung desselben Zeichens für denselben Zweck in String-Literalen.
Nehmen wir an, Sie möchten einen RE schreiben, der die Zeichenkette \section abgleicht, die möglicherweise in einer LaTeX-Datei gefunden wird. Um herauszufinden, was im Programmcode geschrieben werden soll, beginnen Sie mit der gewünschten abzugleichenden Zeichenkette. Als Nächstes müssen Sie alle Backslashes und andere Metazeichen maskieren, indem Sie ihnen einen Backslash voranstellen, was zu der Zeichenkette \\section führt. Die resultierende Zeichenkette, die an re.compile() übergeben werden muss, ist \\section. Um dies jedoch als Python-String-Literal auszudrücken, müssen beide Backslashes *nochmals* maskiert werden.
Zeichen |
Phase |
|---|---|
|
Zu abgleichender Textstring |
|
Maskierter Backslash für |
|
Maskierte Backslashes für ein String-Literal |
Kurz gesagt, um einen Literal-Backslash abzugleichen, muss man '\\\\' als RE-String schreiben, da der reguläre Ausdruck \\ sein muss und jeder Backslash innerhalb eines regulären Python-String-Literals als \\ ausgedrückt werden muss. In REs, die wiederholt Backslashes aufweisen, führt dies zu vielen wiederholten Backslashes und macht die resultierenden Strings schwer verständlich.
Die Lösung besteht darin, die Raw-String-Notation von Python für reguläre Ausdrücke zu verwenden; Backslashes werden in einem String-Literal, dem ein 'r' vorangestellt ist, nicht speziell behandelt, sodass r"\n" ein zweistelliger String ist, der '\' und 'n' enthält, während "\n" ein einstelliger String ist, der einen Zeilenumbruch enthält. Reguläre Ausdrücke werden oft in Python-Code mit dieser Raw-String-Notation geschrieben.
Darüber hinaus führen spezielle Escape-Sequenzen, die in regulären Ausdrücken gültig sind, aber nicht als Python-String-Literale, nun zu einer DeprecationWarning und werden schließlich zu einem SyntaxError, was bedeutet, dass die Sequenzen ungültig werden, wenn keine Raw-String-Notation oder keine Maskierung der Backslashes verwendet wird.
Regulärer String |
Raw String |
|---|---|
|
|
|
|
|
|
Abgleiche durchführen¶
Sobald Sie ein Objekt haben, das einen kompilierten regulären Ausdruck darstellt, was machen Sie damit? Musterobjekte haben mehrere Methoden und Attribute. Nur die wichtigsten werden hier behandelt; konsultieren Sie die re-Dokumentation für eine vollständige Liste.
Methode/Attribut |
Zweck |
|---|---|
|
Bestimmen Sie, ob der RE am Anfang des Strings übereinstimmt. |
|
Durchsuchen Sie einen String und suchen Sie nach jeder Stelle, an der dieser RE übereinstimmt. |
|
Finden Sie alle Unterstrings, bei denen der RE übereinstimmt, und geben Sie sie als Liste zurück. |
|
Finden Sie alle Unterstrings, bei denen der RE übereinstimmt, und geben Sie sie als Iterator zurück. |
match() und search() geben None zurück, wenn keine Übereinstimmung gefunden werden kann. Wenn sie erfolgreich sind, wird eine Instanz eines Match-Objekts zurückgegeben, die Informationen über die Übereinstimmung enthält: wo sie beginnt und endet, den abgeglichenen Unterstring und mehr.
Sie können dies lernen, indem Sie interaktiv mit dem Modul re experimentieren.
Dieses HOWTO verwendet den Standard-Python-Interpreter für seine Beispiele. Führen Sie zuerst den Python-Interpreter aus, importieren Sie das Modul re und kompilieren Sie einen RE
>>> import re
>>> p = re.compile('[a-z]+')
>>> p
re.compile('[a-z]+')
Nun können Sie verschiedene Zeichenketten gegen den RE [a-z]+ abgleichen. Eine leere Zeichenkette sollte überhaupt nicht übereinstimmen, da + „eine oder mehrere Wiederholungen“ bedeutet. match() sollte in diesem Fall None zurückgeben, was dazu führt, dass der Interpreter keine Ausgabe anzeigt. Sie können das Ergebnis von match() explizit ausgeben, um dies klarzustellen.
>>> p.match("")
>>> print(p.match(""))
None
Versuchen wir es nun mit einer Zeichenkette, die übereinstimmen sollte, wie z. B. tempo. In diesem Fall gibt match() ein Match-Objekt zurück, daher sollten Sie das Ergebnis für die spätere Verwendung in einer Variablen speichern.
>>> m = p.match('tempo')
>>> m
<re.Match object; span=(0, 5), match='tempo'>
Jetzt können Sie das Match-Objekt nach Informationen über den übereinstimmenden String abfragen. Match-Objekt-Instanzen haben auch mehrere Methoden und Attribute; die wichtigsten sind
Methode/Attribut |
Zweck |
|---|---|
|
Gibt den vom RE abgeglichenen String zurück |
|
Gibt die Startposition der Übereinstimmung zurück |
|
Gibt die Endposition der Übereinstimmung zurück |
|
Gibt ein Tupel mit den (Start-, End-) Positionen der Übereinstimmung zurück |
Das Ausprobieren dieser Methoden wird ihre Bedeutung bald klären
>>> m.group()
'tempo'
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)
group() gibt den Unterstring zurück, der vom RE abgeglichen wurde. start() und end() geben den Start- und Endindex der Übereinstimmung zurück. span() gibt beide Start- und Endindizes in einem einzigen Tupel zurück. Da die match()-Methode nur prüft, ob der RE am Anfang eines Strings übereinstimmt, ist start() immer null. Die search()-Methode von Mustern durchsucht jedoch den String, sodass die Übereinstimmung in diesem Fall nicht bei null beginnen muss.
>>> print(p.match('::: message'))
None
>>> m = p.search('::: message'); print(m)
<re.Match object; span=(4, 11), match='message'>
>>> m.group()
'message'
>>> m.span()
(4, 11)
In tatsächlichen Programmen ist der häufigste Stil, das Match-Objekt in einer Variablen zu speichern und dann zu prüfen, ob es None ist. Dies sieht normalerweise so aus
p = re.compile( ... )
m = p.match( 'string goes here' )
if m:
print('Match found: ', m.group())
else:
print('No match')
Zwei Muster-Methoden geben alle Übereinstimmungen für ein Muster zurück. findall() gibt eine Liste von übereinstimmenden Strings zurück
>>> p = re.compile(r'\d+')
>>> p.findall('12 drummers drumming, 11 pipers piping, 10 lords a-leaping')
['12', '11', '10']
Das Präfix r, das das Literal zu einem Raw-String-Literal macht, ist in diesem Beispiel erforderlich, da Escape-Sequenzen in einem normalen „gekochten“ String-Literal, die von Python nicht erkannt werden, im Gegensatz zu regulären Ausdrücken, nun zu einer DeprecationWarning und schließlich zu einem SyntaxError führen. Siehe Die Backslash-Plage.
findall() muss die gesamte Liste erstellen, bevor sie als Ergebnis zurückgegeben werden kann. Die Methode finditer() gibt eine Sequenz von Match-Objekt-Instanzen als Iterator zurück
>>> iterator = p.finditer('12 drummers drumming, 11 ... 10 ...')
>>> iterator
<callable_iterator object at 0x...>
>>> for match in iterator:
... print(match.span())
...
(0, 2)
(22, 24)
(29, 31)
Modul-Level-Funktionen¶
Sie müssen kein Pattern-Objekt erstellen und dessen Methoden aufrufen. Das re-Modul stellt auch Top-Level-Funktionen zur Verfügung, die match(), search(), findall(), sub() usw. heißen. Diese Funktionen nehmen die gleichen Argumente wie die entsprechenden Pattern-Methoden entgegen, wobei der RE-String als erstes Argument hinzugefügt wird, und geben immer noch entweder None oder eine Instanz eines Match-Objekts zurück.
>>> print(re.match(r'From\s+', 'Fromage amk'))
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')
<re.Match object; span=(0, 5), match='From '>
Intern erstellt diese Funktion einfach ein Pattern-Objekt für Sie und ruft die entsprechende Methode darauf auf. Sie speichert auch das kompilierte Objekt in einem Cache, sodass zukünftige Aufrufe mit demselben RE den Pattern nicht immer wieder neu parsen müssen.
Sollten Sie diese Modul-Level-Funktionen verwenden oder sich das Pattern holen und selbst seine Methoden aufrufen? Wenn Sie in einer Schleife auf einen regulären Ausdruck zugreifen, spart die Vorabkompilierung einige Funktionsaufrufe. Außerhalb von Schleifen gibt es dank des internen Caches kaum einen Unterschied.
Kompilierungs-Flags¶
Kompilierungs-Flags ermöglichen es Ihnen, einige Aspekte der Funktionsweise von regulären Ausdrücken zu ändern. Flags sind im re-Modul unter zwei Namen verfügbar: einem langen Namen wie IGNORECASE und einer kurzen, einbuchstabigen Form wie I. (Wenn Sie mit Perl-Pattern-Modifikatoren vertraut sind, verwenden die einbuchstabigen Formen dieselben Buchstaben; die Kurzform von re.VERBOSE ist zum Beispiel re.X.) Mehrere Flags können durch bitweises ODER verknüpft werden; re.I | re.M setzt beispielsweise sowohl die I- als auch die M-Flags.
Hier ist eine Tabelle der verfügbaren Flags, gefolgt von einer detaillierteren Erklärung jedes einzelnen.
Flag |
Bedeutung |
|---|---|
Lässt mehrere Escape-Sequenzen wie |
|
Lässt |
|
Führt case-insensitive Übereinstimmungen durch. |
|
Führt eine locale-bewusste Übereinstimmung durch. |
|
Mehrzeilige Übereinstimmung, die |
|
Aktiviert verbose REs, die übersichtlicher und verständlicher organisiert werden können. |
- re.I
- re.IGNORECASE
Führt case-insensitive Übereinstimmungen durch; Zeichenklassen und literale Zeichenketten gleichen Buchstaben ab, indem sie die Groß-/Kleinschreibung ignorieren. Zum Beispiel passt
[A-Z]auch auf Kleinbuchstaben. Vollständige Unicode-Übereinstimmungen funktionieren ebenfalls, es sei denn, dasASCII-Flag wird verwendet, um Nicht-ASCII-Übereinstimmungen zu deaktivieren. Wenn die Unicode-Patterns[a-z]oder[A-Z]in Kombination mit demIGNORECASE-Flag verwendet werden, passen sie auf die 52 ASCII-Buchstaben und 4 zusätzliche Nicht-ASCII-Buchstaben: 'İ' (U+0130, Lateinischer Großbuchstabe I mit Punkt darüber), 'ı' (U+0131, Lateinischer Kleinbuchstabe i ohne Punkt), 'ſ' (U+017F, Lateinischer Kleinbuchstabe langes s) und 'K' (U+212A, Kelvin-Zeichen).Spampasst auf'Spam','spam','spAM'oder'ſpam'(letzteres wird nur im Unicode-Modus abgeglichen). Diese Umwandlung in Kleinbuchstaben berücksichtigt nicht die aktuelle Locale; dies geschieht, wenn Sie auch dasLOCALE-Flag setzen.
- re.L
- re.LOCALE
Macht
\w,\W,\b,\Bund case-insensitive Übereinstimmungen abhängig von der aktuellen Locale statt der Unicode-Datenbank.Locales sind eine Funktion der C-Bibliothek, die dazu dient, Programme zu schreiben, die Sprachunterschiede berücksichtigen. Wenn Sie beispielsweise kodierten französischen Text verarbeiten, möchten Sie
\w+verwenden können, um Wörter abzugleichen, aber\wgleicht bei Byte-Mustern nur die Zeichenklasse[A-Za-z]ab; es gleicht keine Bytes ab, dieéoderçentsprechen. Wenn Ihr System ordnungsgemäß konfiguriert ist und eine französische Locale ausgewählt ist, teilen bestimmte C-Funktionen dem Programm mit, dass das Byte, daséentspricht, ebenfalls als Buchstabe betrachtet werden sollte. Das Setzen desLOCALE-Flags beim Kompilieren eines regulären Ausdrucks führt dazu, dass das resultierende kompilierte Objekt diese C-Funktionen für\wverwendet. Dies ist langsamer, ermöglicht aber auch, dass\w+französische Wörter wie erwartet abgleicht. Die Verwendung dieses Flags wird in Python 3 nicht empfohlen, da der Locale-Mechanismus sehr unzuverlässig ist, nur eine "Kultur" gleichzeitig verarbeitet und nur mit 8-Bit-Locales funktioniert. Unicode-Abgleich ist in Python 3 für Unicode (str)-Muster bereits standardmäßig aktiviert und kann verschiedene Locales/Sprachen verarbeiten.
- re.M
- re.MULTILINE
(
^und$wurden noch nicht erklärt; sie werden in Abschnitt Weitere Metazeichen eingeführt.)Normalerweise passt
^nur am Anfang des Strings, und$passt nur am Ende des Strings und unmittelbar vor dem Zeilenumbruch (falls vorhanden) am Ende des Strings. Wenn dieses Flag gesetzt ist, passt^am Anfang des Strings und am Anfang jeder Zeile innerhalb des Strings, unmittelbar nach jedem Zeilenumbruch. Ebenso passt das Metazeichen$entweder am Ende des Strings und am Ende jeder Zeile (unmittelbar vor jedem Zeilenumbruch).
- re.S
- re.DOTALL
Lässt das Sonderzeichen
'.'jedes Zeichen abgleichen, einschließlich eines Zeilenumbruchs; ohne dieses Flag passt'.'alles außer einem Zeilenumbruch.
- re.A
- re.ASCII
Lässt
\w,\W,\b,\B,\sund\SASCII-only-Abgleiche statt vollständige Unicode-Abgleiche durchführen. Dies ist nur für Unicode-Muster sinnvoll und wird für Byte-Muster ignoriert.
- re.X
- re.VERBOSE
Dieses Flag ermöglicht es Ihnen, reguläre Ausdrücke zu schreiben, die besser lesbar sind, indem es Ihnen mehr Flexibilität bei der Formatierung gibt. Wenn dieses Flag gesetzt ist, werden Leerzeichen innerhalb des RE-Strings ignoriert, es sei denn, die Leerzeichen befinden sich in einer Zeichenklasse oder werden von einem nicht maskierten Backslash vorangestellt; dies ermöglicht es Ihnen, den RE klarer zu organisieren und einzurücken. Dieses Flag ermöglicht auch, Kommentare innerhalb eines RE einzufügen, die vom Engine ignoriert werden; Kommentare werden durch ein
'#'gekennzeichnet, das sich weder in einer Zeichenklasse befindet noch von einem nicht maskierten Backslash vorangestellt wird.Zum Beispiel ist hier ein RE, der
re.VERBOSEverwendet; sehen Sie, wie viel leichter er zu lesen ist?charref = re.compile(r""" &[#] # Start of a numeric entity reference ( 0[0-7]+ # Octal form | [0-9]+ # Decimal form | x[0-9a-fA-F]+ # Hexadecimal form ) ; # Trailing semicolon """, re.VERBOSE)
Ohne die verbose-Einstellung würde der RE so aussehen
charref = re.compile("&#(0[0-7]+" "|[0-9]+" "|x[0-9a-fA-F]+);")
Im obigen Beispiel wurde die automatische Verkettung von String-Literalen von Python verwendet, um den RE in kleinere Teile zu zerlegen, aber er ist immer noch schwerer zu verstehen als die Version mit
re.VERBOSE.
Mehr Pattern-Power¶
Bisher haben wir nur einen Teil der Features von regulären Ausdrücken behandelt. In diesem Abschnitt werden wir einige neue Metazeichen behandeln und wie man Gruppen verwendet, um Teile des abgeglichenen Textes abzurufen.
Weitere Metazeichen¶
Es gibt einige Metazeichen, die wir noch nicht behandelt haben. Die meisten davon werden in diesem Abschnitt behandelt.
Einige der verbleibenden Metazeichen, die diskutiert werden, sind *Null-Breiten-Assertionen* (zero-width assertions). Sie bewirken nicht, dass der Engine durch den String vorrückt; stattdessen verbrauchen sie keine Zeichen und sind einfach erfolgreich oder schlagen fehl. Zum Beispiel ist \b eine Assertion, dass sich die aktuelle Position an einer Wortgrenze befindet; die Position wird durch das \b überhaupt nicht verändert. Das bedeutet, dass Null-Breiten-Assertionen niemals wiederholt werden sollten, denn wenn sie einmal an einer bestimmten Stelle übereinstimmen, können sie offensichtlich unendlich oft übereinstimmen.
|Alternation oder der "oder"-Operator. Wenn *A* und *B* reguläre Ausdrücke sind, gleicht
A|Bjeden String ab, der entweder *A* oder *B* abgleicht.|hat eine sehr niedrige Priorität, damit es vernünftig funktioniert, wenn Sie mehr als eine Zeichenkette alternieren.Crow|Servogleicht entweder'Crow'oder'Servo'ab, nicht'Cro', ein'w'oder ein'S'und'ervo'.Um ein literales
'|'abzugleichen, verwenden Sie\|oder schließen Sie es in eine Zeichenklasse ein, wie in[|].^Passt am Anfang von Zeilen. Sofern das
MULTILINE-Flag nicht gesetzt ist, passt dies nur am Anfang des Strings. ImMULTILINE-Modus passt dies auch unmittelbar nach jedem Zeilenumbruch innerhalb des Strings.Wenn Sie beispielsweise das Wort
Fromnur am Anfang einer Zeile abgleichen möchten, ist der zu verwendende RE^From.>>> print(re.search('^From', 'From Here to Eternity')) <re.Match object; span=(0, 4), match='From'> >>> print(re.search('^From', 'Reciting From Memory')) None
Um ein literales
'^'abzugleichen, verwenden Sie\^.$Passt am Ende einer Zeile, die als entweder das Ende des Strings oder jede Stelle definiert ist, auf die ein Zeilenumbruchzeichen folgt.
>>> print(re.search('}$', '{block}')) <re.Match object; span=(6, 7), match='}'> >>> print(re.search('}$', '{block} ')) None >>> print(re.search('}$', '{block}\n')) <re.Match object; span=(6, 7), match='}'>
Um ein literales
'$'abzugleichen, verwenden Sie\$oder schließen Sie es in eine Zeichenklasse ein, wie in[$].\APasst nur am Anfang des Strings. Wenn nicht im
MULTILINE-Modus, sind\Aund^effektiv gleich. ImMULTILINE-Modus sind sie unterschiedlich:\Apasst immer noch nur am Anfang des Strings, aber^kann an jeder Stelle innerhalb des Strings passen, die auf einen Zeilenumbruch folgt.\zPasst nur am Ende des Strings.
\ZDas Gleiche wie
\z. Zur Kompatibilität mit älteren Python-Versionen.\bWortgrenze. Dies ist eine Null-Breiten-Assertion, die nur am Anfang oder Ende eines Wortes passt. Ein Wort wird als eine Folge von alphanumerischen Zeichen definiert, sodass das Ende eines Wortes durch Leerzeichen oder ein nicht-alphanumerisches Zeichen angezeigt wird.
Das folgende Beispiel gleicht
classnur ab, wenn es ein vollständiges Wort ist; es passt nicht, wenn es in einem anderen Wort enthalten ist.>>> p = re.compile(r'\bclass\b') >>> print(p.search('no class at all')) <re.Match object; span=(3, 8), match='class'> >>> print(p.search('the declassified algorithm')) None >>> print(p.search('one subclass is')) None
Es gibt zwei Feinheiten, die Sie bei der Verwendung dieser speziellen Sequenz beachten sollten. Erstens ist dies die schlimmste Kollision zwischen den String-Literalen von Python und den Sequenzen von regulären Ausdrücken. In den String-Literalen von Python ist
\bdas Backspace-Zeichen, ASCII-Wert 8. Wenn Sie keine Roh-Strings verwenden, konvertiert Python\bin ein Backspace, und Ihr RE passt nicht wie erwartet. Das folgende Beispiel sieht genauso aus wie unser vorheriger RE, lässt aber das'r'vor dem RE-String weg.>>> p = re.compile('\bclass\b') >>> print(p.search('no class at all')) None >>> print(p.search('\b' + 'class' + '\b')) <re.Match object; span=(0, 7), match='\x08class\x08'>
Zweitens, innerhalb einer Zeichenklasse, wo diese Assertion keinen Zweck hat, repräsentiert
\bdas Backspace-Zeichen, zur Kompatibilität mit den String-Literalen von Python.\BEine weitere Null-Breiten-Assertion, dies ist das Gegenteil von
\b, passt nur, wenn die aktuelle Position nicht an einer Wortgrenze liegt.
Gruppierung¶
Häufig benötigen Sie mehr Informationen als nur, ob der RE übereingestimmt hat oder nicht. Reguläre Ausdrücke werden oft zum Zerlegen von Strings verwendet, indem ein RE geschrieben wird, der in mehrere Untergruppen unterteilt ist, die verschiedene interessierende Komponenten abgleichen. Zum Beispiel ist eine RFC-822-Headerzeile in einen Header-Namen und einen Wert unterteilt, getrennt durch einen Doppelpunkt ':', wie hier
From: author@example.com
User-Agent: Thunderbird 1.5.0.9 (X11/20061227)
MIME-Version: 1.0
To: editor@example.com
Dies kann durch Schreiben eines regulären Ausdrucks behandelt werden, der eine gesamte Headerzeile abgleicht und eine Gruppe hat, die den Header-Namen abgleicht, und eine weitere Gruppe, die den Wert des Headers abgleicht.
Gruppen werden durch die Metazeichen '(' und ')' gekennzeichnet. '(' und ')' haben ziemlich dieselbe Bedeutung wie in mathematischen Ausdrücken; sie gruppieren die darin enthaltenen Ausdrücke, und Sie können den Inhalt einer Gruppe mit einem Quantifizierer wie *, +, ? oder {m,n} wiederholen. Zum Beispiel passt (ab)* auf null oder mehr Wiederholungen von ab.
>>> p = re.compile('(ab)*')
>>> print(p.match('ababababab').span())
(0, 10)
Gruppen, die mit '(' und ')' gekennzeichnet sind, erfassen auch den Start- und Endindex des von ihnen abgeglichenen Textes; dies kann durch Übergabe eines Arguments an group(), start(), end() und span() abgerufen werden. Gruppen werden beginnend mit 0 nummeriert. Gruppe 0 ist immer vorhanden; sie ist der gesamte RE, sodass alle Methoden des Match-Objekts Gruppe 0 als ihr Standardargument haben. Später werden wir sehen, wie Gruppen ausgedrückt werden können, die den abgeglichenen Textbereich nicht erfassen.
>>> p = re.compile('(a)b')
>>> m = p.match('ab')
>>> m.group()
'ab'
>>> m.group(0)
'ab'
Untergruppen werden von links nach rechts nummeriert, beginnend bei 1. Gruppen können verschachtelt sein; um die Nummer zu bestimmen, zählen Sie einfach die öffnenden Klammern von links nach rechts.
>>> p = re.compile('(a(b)c)d')
>>> m = p.match('abcd')
>>> m.group(0)
'abcd'
>>> m.group(1)
'abc'
>>> m.group(2)
'b'
group() kann mehrere Gruppennummern gleichzeitig erhalten, in diesem Fall gibt es ein Tupel mit den entsprechenden Werten für diese Gruppen zurück.
>>> m.group(2,1,2)
('b', 'abc', 'b')
Die Methode groups() gibt ein Tupel mit den Strings aller Untergruppen zurück, von 1 bis zur maximal vorhandenen Anzahl.
>>> m.groups()
('abc', 'b')
Backreferences in einem Pattern ermöglichen es Ihnen anzugeben, dass der Inhalt einer früheren erfassenden Gruppe ebenfalls an der aktuellen Position im String gefunden werden muss. Zum Beispiel ist \1 erfolgreich, wenn der exakte Inhalt von Gruppe 1 an der aktuellen Position gefunden werden kann, und schlägt andernfalls fehl. Denken Sie daran, dass auch die String-Literale von Python einen Backslash gefolgt von Zahlen verwenden, um beliebige Zeichen in einen String einzufügen. Stellen Sie also sicher, dass Sie einen Roh-String verwenden, wenn Sie Backreferences in einem RE einfügen.
Zum Beispiel erkennt der folgende RE doppelte Wörter in einem String.
>>> p = re.compile(r'\b(\w+)\s+\1\b')
>>> p.search('Paris in the the spring').group()
'the the'
Backreferences wie diese sind nicht oft nützlich, um nur einen String zu durchsuchen - es gibt wenige Textformate, die Daten auf diese Weise wiederholen -, aber Sie werden bald feststellen, dass sie *sehr* nützlich sind, wenn Sie String-Ersetzungen durchführen.
Nicht-erfassende und benannte Gruppen¶
Ausgefeilte REs können viele Gruppen verwenden, sowohl um Teile von Interesse zu erfassen, als auch um den RE selbst zu gruppieren und zu strukturieren. Bei komplexen REs wird es schwierig, die Gruppennummern zu verfolgen. Es gibt zwei Features, die bei diesem Problem helfen. Beide verwenden eine gemeinsame Syntax für Erweiterungen von regulären Ausdrücken, also schauen wir uns das zuerst an.
Perl 5 ist bekannt für seine mächtigen Ergänzungen zu Standard-Regulären Ausdrücken. Für diese neuen Features konnten die Perl-Entwickler keine neuen Ein-Tasten-Metazeichen oder neue spezielle Sequenzen, die mit \ beginnen, wählen, ohne Perl-Reguläre Ausdrücke verwirrend anders als Standard-REs zu machen. Wenn sie zum Beispiel & als neues Metazeichen wählten, würden alte Ausdrücke davon ausgehen, dass & ein reguläres Zeichen ist und es nicht durch Schreiben von \& oder [&] maskiert hätten.
Die von den Perl-Entwicklern gewählte Lösung war die Verwendung von (?...) als Erweiterungssyntax. Ein ? unmittelbar nach einer Klammer war ein Syntaxfehler, da die ? nichts zu wiederholen hätte, sodass dies keine Kompatibilitätsprobleme verursachte. Die Zeichen unmittelbar nach dem ? geben an, welche Erweiterung verwendet wird, sodass (?=foo) eine Sache ist (eine positive Lookahead-Assertion) und (?:foo) etwas anderes (eine nicht-erfassende Gruppe, die den Unterausdruck foo enthält).
Python unterstützt mehrere von Perl's Erweiterungen und fügt eine Erweiterungssyntax zur Erweiterungssyntax von Perl hinzu. Wenn das erste Zeichen nach dem Fragezeichen ein P ist, wissen Sie, dass es sich um eine Erweiterung handelt, die spezifisch für Python ist.
Nachdem wir nun die allgemeine Erweiterungssyntax betrachtet haben, können wir zu den Funktionen zurückkehren, die die Arbeit mit Gruppen in komplexen REs vereinfachen.
Manchmal möchten Sie eine Gruppe verwenden, um einen Teil eines regulären Ausdrucks zu bezeichnen, sind aber nicht daran interessiert, den Inhalt der Gruppe abzurufen. Sie können dies explizit machen, indem Sie eine nicht-erfassende Gruppe verwenden: (?:...), wobei Sie ... durch einen beliebigen anderen regulären Ausdruck ersetzen können.
>>> m = re.match("([abc])+", "abc")
>>> m.groups()
('c',)
>>> m = re.match("(?:[abc])+", "abc")
>>> m.groups()
()
Abgesehen davon, dass Sie den Inhalt des abgeglichenen Elements nicht abrufen können, verhält sich eine nicht-erfassende Gruppe genauso wie eine erfassende Gruppe; Sie können alles darin platzieren, sie mit einem Wiederholungsmetazeichen wie * wiederholen und sie innerhalb anderer Gruppen (erfassend oder nicht-erfassend) verschachteln. (?:...) ist besonders nützlich, wenn ein bestehendes Pattern modifiziert wird, da Sie neue Gruppen hinzufügen können, ohne die Nummerierung aller anderen Gruppen zu ändern. Es sei erwähnt, dass es keinen Leistungsunterschied beim Suchen zwischen erfassenden und nicht-erfassenden Gruppen gibt; keine Form ist schneller als die andere.
Ein wichtiges Feature sind benannte Gruppen: anstatt sie anhand von Nummern zu referenzieren, können Gruppen anhand eines Namens referenziert werden.
Die Syntax für eine benannte Gruppe sind einige der Python-spezifischen Erweiterungen: (?P<name>...). *name* ist offensichtlich der Name der Gruppe. Benannte Gruppen verhalten sich genau wie erfassende Gruppen und assoziieren zusätzlich einen Namen mit einer Gruppe. Die Match-Objekt-Methoden, die sich mit erfassenden Gruppen befassen, akzeptieren entweder ganze Zahlen, die die Gruppe nach Nummer referenzieren, oder Strings, die den gewünschten Gruppennamen enthalten. Benannte Gruppen erhalten immer noch Nummern, sodass Sie Informationen über eine Gruppe auf zwei Arten abrufen können
>>> p = re.compile(r'(?P<word>\b\w+\b)')
>>> m = p.search( '(((( Lots of punctuation )))' )
>>> m.group('word')
'Lots'
>>> m.group(1)
'Lots'
Zusätzlich können Sie benannte Gruppen als Wörterbuch mit groupdict() abrufen
>>> m = re.match(r'(?P<first>\w+) (?P<last>\w+)', 'Jane Doe')
>>> m.groupdict()
{'first': 'Jane', 'last': 'Doe'}
Benannte Gruppen sind praktisch, da sie Ihnen erlauben, leicht zu merkende Namen zu verwenden, anstatt sich Zahlen merken zu müssen. Hier ist ein Beispiel-RE aus dem imaplib-Modul
InternalDate = re.compile(r'INTERNALDATE "'
r'(?P<day>[ 123][0-9])-(?P<mon>[A-Z][a-z][a-z])-'
r'(?P<year>[0-9][0-9][0-9][0-9])'
r' (?P<hour>[0-9][0-9]):(?P<min>[0-9][0-9]):(?P<sec>[0-9][0-9])'
r' (?P<zonen>[-+])(?P<zoneh>[0-9][0-9])(?P<zonem>[0-9][0-9])'
r'"')
Es ist offensichtlich viel einfacher, m.group('zonem') abzurufen, anstatt sich daran erinnern zu müssen, Gruppe 9 abzurufen.
Die Syntax für Backreferences in einem Ausdruck wie (...)\1 bezieht sich auf die Nummer der Gruppe. Es gibt natürlich eine Variante, die den Gruppennamen anstelle der Nummer verwendet. Dies ist eine weitere Python-Erweiterung: (?P=name) gibt an, dass der Inhalt der Gruppe namens *name* erneut an der aktuellen Stelle abgeglichen werden soll. Der reguläre Ausdruck zum Auffinden doppelter Wörter, \b(\w+)\s+\1\b, kann auch als \b(?P<word>\w+)\s+(?P=word)\b geschrieben werden.
>>> p = re.compile(r'\b(?P<word>\w+)\s+(?P=word)\b')
>>> p.search('Paris in the the spring').group()
'the the'
Lookahead-Assertions¶
Eine weitere Null-Breiten-Assertion ist die Lookahead-Assertion. Lookahead-Assertionen sind sowohl in positiver als auch in negativer Form verfügbar und sehen so aus
(?=...)Positive Lookahead-Assertion. Dies ist erfolgreich, wenn der enthaltene reguläre Ausdruck, hier durch
...dargestellt, an der aktuellen Position erfolgreich übereinstimmt, und schlägt andernfalls fehl. Aber nachdem der enthaltene Ausdruck ausprobiert wurde, rückt die Matching-Engine überhaupt nicht vor; der Rest des Patterns wird genau dort ausprobiert, wo die Assertion begann.(?!...)Negative Lookahead-Assertion. Dies ist das Gegenteil der positiven Assertion; sie ist erfolgreich, wenn der enthaltene Ausdruck an der aktuellen Position im String *nicht* übereinstimmt.
Um dies konkret zu machen, betrachten wir einen Fall, in dem ein Lookahead nützlich ist. Betrachten Sie ein einfaches Pattern, um einen Dateinamen abzugleichen und ihn in einen Basisnamen und eine Erweiterung aufzuteilen, die durch einen Punkt . getrennt sind. Zum Beispiel in news.rc ist news der Basisname und rc ist die Erweiterung der Datei.
Das Pattern, um dies abzugleichen, ist ziemlich einfach
.*[.].*$
Beachten Sie, dass der . besonders behandelt werden muss, da es sich um ein Metazeichen handelt, und daher in einer Zeichenklasse steht, um nur dieses spezifische Zeichen abzugleichen. Beachten Sie auch das nachgestellte $; dies wird hinzugefügt, um sicherzustellen, dass der Rest der Zeichenkette in die Erweiterung einbezogen werden muss. Dieses reguläre Ausdrucksmuster gleicht foo.bar und autoexec.bat und sendmail.cf und printers.conf ab.
Betrachten wir nun eine leichte Komplizierung des Problems; was, wenn Sie Dateinamen abgleichen möchten, bei denen die Erweiterung nicht bat ist? Einige fehlerhafte Versuche
.*[.][^b].*$
Der erste Versuch schließt bat aus, indem gefordert wird, dass das erste Zeichen der Erweiterung kein b ist. Dies ist falsch, da das Muster auch foo.bar nicht abgleicht.
.*[.]([^b]..|.[^a].|..[^t])$
Der Ausdruck wird unordentlicher, wenn Sie versuchen, die erste Lösung zu reparieren, indem Sie fordern, dass einer der folgenden Fälle übereinstimmt: das erste Zeichen der Erweiterung ist kein b; das zweite Zeichen ist kein a; oder das dritte Zeichen ist kein t. Dies akzeptiert foo.bar und lehnt autoexec.bat ab, erfordert jedoch eine dreibuchstabige Erweiterung und akzeptiert keine Dateinamen mit einer zweibuchstabigen Erweiterung wie sendmail.cf. Wir werden das Muster erneut komplizieren, um es zu beheben.
.*[.]([^b].?.?|.[^a]?.?|..?[^t]?)$
Im dritten Versuch werden die zweite und dritte Buchstabe optional gemacht, um auch Erweiterungen mit weniger als drei Zeichen abzugleichen, wie z. B. sendmail.cf.
Das Muster wird jetzt wirklich kompliziert, was es schwer lesbar und verständlich macht. Schlimmer noch, wenn sich das Problem ändert und Sie sowohl bat als auch exe als Erweiterungen ausschließen möchten, würde das Muster noch komplizierter und verwirrender werden.
Ein negativer Lookahead durchbricht all diese Verwirrung
.*[.](?!bat$)[^.]*$
Der negative Lookahead bedeutet: Wenn der Ausdruck bat zu diesem Zeitpunkt nicht übereinstimmt, versuchen Sie den Rest des Musters; wenn bat$ übereinstimmt, schlägt das gesamte Muster fehl. Das nachgestellte $ ist erforderlich, um sicherzustellen, dass etwas wie sample.batch, bei dem die Erweiterung nur mit bat beginnt, zulässig ist. Die [^.]* stellt sicher, dass das Muster funktioniert, wenn mehrere Punkte im Dateinamen vorhanden sind.
Das Ausschließen einer weiteren Dateiendung ist nun einfach; fügen Sie sie einfach als Alternative in die Assertion ein. Das folgende Muster schließt Dateinamen aus, die auf bat oder exe enden
.*[.](?!bat$|exe$)[^.]*$
Strings modifizieren¶
Bis zu diesem Punkt haben wir einfach Suchen gegen einen statischen String durchgeführt. Reguläre Ausdrücke werden auch häufig verwendet, um Strings auf verschiedene Weise zu modifizieren, indem die folgenden Muster-Methoden verwendet werden:
Methode/Attribut |
Zweck |
|---|---|
|
Teilt den String in eine Liste auf, indem er dort geteilt wird, wo der RE übereinstimmt |
|
Findet alle Teilstrings, bei denen der RE übereinstimmt, und ersetzt sie durch einen anderen String |
|
Macht dasselbe wie |
Strings aufteilen¶
Die split()-Methode eines Musters teilt einen String dort auf, wo der RE übereinstimmt, und gibt eine Liste der Teile zurück. Sie ähnelt der split()-Methode von Strings, bietet jedoch viel mehr Allgemeinheit bei den Trennzeichen, nach denen Sie aufteilen können; String split() unterstützt nur das Aufteilen nach Leerzeichen oder einem festen String. Wie Sie erwarten würden, gibt es auch eine re.split()-Funktion auf Modulebene.
- .split(string[, maxsplit=0])
Teilt string nach den Übereinstimmungen des regulären Ausdrucks auf. Wenn erfassende Klammern im RE verwendet werden, werden deren Inhalte ebenfalls als Teil der resultierenden Liste zurückgegeben. Wenn maxsplit nicht Null ist, werden höchstens maxsplit Teilungen durchgeführt.
Sie können die Anzahl der durchgeführten Teilungen begrenzen, indem Sie einen Wert für maxsplit übergeben. Wenn maxsplit nicht Null ist, werden höchstens maxsplit Teilungen durchgeführt, und der Rest des Strings wird als letztes Element der Liste zurückgegeben. Im folgenden Beispiel ist das Trennzeichen jede Sequenz von nicht-alphanumerischen Zeichen.
>>> p = re.compile(r'\W+')
>>> p.split('This is a test, short and sweet, of split().')
['This', 'is', 'a', 'test', 'short', 'and', 'sweet', 'of', 'split', '']
>>> p.split('This is a test, short and sweet, of split().', 3)
['This', 'is', 'a', 'test, short and sweet, of split().']
Manchmal sind Sie nicht nur an dem Text zwischen den Trennzeichen interessiert, sondern müssen auch wissen, was das Trennzeichen war. Wenn erfassende Klammern im RE verwendet werden, werden deren Werte ebenfalls als Teil der Liste zurückgegeben. Vergleichen Sie die folgenden Aufrufe
>>> p = re.compile(r'\W+')
>>> p2 = re.compile(r'(\W+)')
>>> p.split('This... is a test.')
['This', 'is', 'a', 'test', '']
>>> p2.split('This... is a test.')
['This', '... ', 'is', ' ', 'a', ' ', 'test', '.', '']
Die Funktion re.split() auf Modulebene fügt den zu verwendenden RE als erstes Argument hinzu, ist aber ansonsten gleich.
>>> re.split(r'[\W]+', 'Words, words, words.')
['Words', 'words', 'words', '']
>>> re.split(r'([\W]+)', 'Words, words, words.')
['Words', ', ', 'words', ', ', 'words', '.', '']
>>> re.split(r'[\W]+', 'Words, words, words.', 1)
['Words', 'words, words.']
Suchen und Ersetzen¶
Eine weitere gängige Aufgabe ist es, alle Übereinstimmungen für ein Muster zu finden und sie durch einen anderen String zu ersetzen. Die sub()-Methode nimmt einen Ersatzwert, der entweder ein String oder eine Funktion sein kann, und den zu verarbeitenden String.
- .sub(replacement, string[, count=0])
Gibt den String zurück, der durch Ersetzen der linken nicht überlappenden Vorkommen des RE in string durch den Ersatz replacement erhalten wird. Wenn das Muster nicht gefunden wird, wird string unverändert zurückgegeben.
Das optionale Argument count ist die maximale Anzahl der zu ersetzenden Muster-Vorkommen; count muss eine nicht-negative Ganzzahl sein. Der Standardwert 0 bedeutet, alle Vorkommen zu ersetzen.
Hier ist ein einfaches Beispiel für die Verwendung der sub()-Methode. Sie ersetzt Farbnamen durch das Wort colour
>>> p = re.compile('(blue|white|red)')
>>> p.sub('colour', 'blue socks and red shoes')
'colour socks and colour shoes'
>>> p.sub('colour', 'blue socks and red shoes', count=1)
'colour socks and red shoes'
Die subn()-Methode leistet die gleiche Arbeit, gibt aber ein 2-Tupel zurück, das den neuen String-Wert und die Anzahl der durchgeführten Ersetzungen enthält
>>> p = re.compile('(blue|white|red)')
>>> p.subn('colour', 'blue socks and red shoes')
('colour socks and colour shoes', 2)
>>> p.subn('colour', 'no colours at all')
('no colours at all', 0)
Leere Übereinstimmungen werden nur ersetzt, wenn sie nicht an eine vorherige leere Übereinstimmung angrenzen.
>>> p = re.compile('x*')
>>> p.sub('-', 'abxd')
'-a-b--d-'
Wenn replacement ein String ist, werden alle Backslash-Escapes darin verarbeitet. Das heißt, \n wird in ein einzelnes Zeilenumbruchzeichen umgewandelt, \r in einen Wagenrücklauf und so weiter. Unbekannte Escapes wie \& bleiben unverändert. Backreferences, wie \6, werden durch den Teilstring ersetzt, der von der entsprechenden Gruppe im RE abgeglichen wurde. Dies ermöglicht es Ihnen, Teile des ursprünglichen Textes in den resultierenden Ersatzstring aufzunehmen.
Dieses Beispiel gleicht das Wort section gefolgt von einem String, der in { und } eingeschlossen ist, ab und ändert section in subsection
>>> p = re.compile('section{ ( [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First} section{second}')
'subsection{First} subsection{second}'
Es gibt auch eine Syntax, um benannte Gruppen zu referenzieren, wie sie durch die (?P<name>...)-Syntax definiert sind. \g<name> verwendet den von der Gruppe mit dem Namen name abgeglichenen Teilstring, und \g<number> verwendet die entsprechende Gruppennummer. \g<2> ist daher äquivalent zu \2, ist aber in einem Ersatzstring wie \g<2>0 nicht mehrdeutig. (\20 würde als Referenz auf Gruppe 20 interpretiert werden, nicht als Referenz auf Gruppe 2, gefolgt vom Literalzeichen '0'.) Die folgenden Ersetzungen sind alle äquivalent, verwenden aber alle drei Variationen des Ersatzstrings.
>>> p = re.compile('section{ (?P<name> [^}]* ) }', re.VERBOSE)
>>> p.sub(r'subsection{\1}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<1>}','section{First}')
'subsection{First}'
>>> p.sub(r'subsection{\g<name>}','section{First}')
'subsection{First}'
replacement kann auch eine Funktion sein, die Ihnen noch mehr Kontrolle gibt. Wenn replacement eine Funktion ist, wird die Funktion für jedes nicht überlappende Vorkommen von pattern aufgerufen. Bei jedem Aufruf wird der Funktion ein Match-Objekt als Argument für die Übereinstimmung übergeben und sie kann diese Informationen verwenden, um den gewünschten Ersatzstring zu berechnen und zurückzugeben.
Im folgenden Beispiel übersetzt die Ersatzfunktion Dezimalzahlen in Hexadezimalzahlen
>>> def hexrepl(match):
... "Return the hex string for a decimal number"
... value = int(match.group())
... return hex(value)
...
>>> p = re.compile(r'\d+')
>>> p.sub(hexrepl, 'Call 65490 for printing, 49152 for user code.')
'Call 0xffd2 for printing, 0xc000 for user code.'
Bei Verwendung der Funktion re.sub() auf Modulebene wird das Muster als erstes Argument übergeben. Das Muster kann als Objekt oder als String angegeben werden; wenn Sie reguläre Ausdrucks-Flags angeben müssen, müssen Sie entweder ein Musterobjekt als erstes Argument verwenden oder eingebettete Modifikatoren im Musterstring verwenden, z. B. sub("(?i)b+", "x", "bbbb BBBB") gibt 'x x' zurück.
Häufige Probleme¶
Reguläre Ausdrücke sind ein mächtiges Werkzeug für einige Anwendungen, aber in gewisser Hinsicht ist ihr Verhalten nicht intuitiv und manchmal verhalten sie sich nicht so, wie Sie es erwarten würden. Dieser Abschnitt wird auf einige der häufigsten Fallstricke hinweisen.
String-Methoden verwenden¶
Manchmal ist die Verwendung des re-Moduls ein Fehler. Wenn Sie einen festen String oder eine einzelne Zeichenklasse abgleichen und keine re-Funktionen wie das IGNORECASE-Flag verwenden, ist die volle Leistungsfähigkeit regulärer Ausdrücke möglicherweise nicht erforderlich. Strings haben mehrere Methoden für Operationen mit festen Strings, und sie sind normalerweise viel schneller, da die Implementierung eine einzige kleine C-Schleife ist, die für den Zweck optimiert wurde, anstatt der große, allgemeinere reguläre Ausdrucks-Engine.
Ein Beispiel könnte das Ersetzen eines einzelnen festen Strings durch einen anderen sein; zum Beispiel könnten Sie word durch deed ersetzen. re.sub() scheint die zu verwendende Funktion zu sein, aber betrachten Sie die replace()-Methode. Beachten Sie, dass replace() auch word innerhalb von Wörtern ersetzt, wodurch swordfish zu sdeedfish wird, aber der naive RE word hätte das auch getan. (Um die Ersetzung in Wortteilen zu vermeiden, müsste das Muster \bword\b lauten, um zu fordern, dass word eine Wortgrenze auf beiden Seiten hat. Dies bringt die Aufgabe über die Fähigkeiten von replace() hinaus.)
Eine weitere gängige Aufgabe ist das Löschen jedes Vorkommens eines einzelnen Zeichens aus einem String oder das Ersetzen durch ein anderes einzelnes Zeichen. Dies könnten Sie mit etwas wie re.sub('\n', ' ', S) tun, aber translate() kann beide Aufgaben erledigen und ist schneller als jede Operation mit regulären Ausdrücken.
Kurz gesagt, bevor Sie sich dem re-Modul zuwenden, überlegen Sie, ob Ihr Problem mit einer schnelleren und einfacheren String-Methode gelöst werden kann.
match() vs. search()¶
Die Funktion match() prüft nur, ob der RE am Anfang des Strings übereinstimmt, während search() den String auf Übereinstimmungen durchsucht. Es ist wichtig, diesen Unterschied zu beachten. Denken Sie daran, match() meldet nur eine erfolgreiche Übereinstimmung, die bei 0 beginnt; wenn die Übereinstimmung nicht bei Null beginnt, meldet match() sie *nicht*.
>>> print(re.match('super', 'superstition').span())
(0, 5)
>>> print(re.match('super', 'insuperable'))
None
Andererseits durchsucht search() den String und meldet die erste gefundene Übereinstimmung.
>>> print(re.search('super', 'superstition').span())
(0, 5)
>>> print(re.search('super', 'insuperable').span())
(2, 7)
Manchmal sind Sie versucht, weiterhin re.match() zu verwenden und einfach .* am Anfang Ihres REs hinzuzufügen. Widerstehen Sie dieser Versuchung und verwenden Sie stattdessen re.search(). Der reguläre Ausdruckskomplierer führt einige Analysen von REs durch, um den Prozess der Suche nach einer Übereinstimmung zu beschleunigen. Eine solche Analyse ermittelt, wie das erste Zeichen einer Übereinstimmung aussehen muss; beispielsweise muss ein Muster, das mit Crow beginnt, mit einem 'C' beginnen. Die Analyse ermöglicht es der Engine, schnell durch den String zu scannen und nach dem Anfangszeichen zu suchen, und nur dann die vollständige Übereinstimmung zu versuchen, wenn ein 'C' gefunden wird.
Das Hinzufügen von .* macht diese Optimierung zunichte und erfordert das Scannen bis zum Ende des Strings und dann das Zurückverfolgen, um eine Übereinstimmung für den Rest des RE zu finden. Verwenden Sie stattdessen re.search().
Gierig vs. Nicht-Gierig¶
Beim Wiederholen eines regulären Ausdrucks, wie in a*, besteht die daraus resultierende Aktion darin, so viel wie möglich vom Muster zu verbrauchen. Diese Tatsache schlägt oft zu, wenn Sie versuchen, ein Paar von ausgeglichenen Trennzeichen abzugleichen, wie die Winkelklammern, die ein HTML-Tag umschließen. Das naive Muster zum Abgleichen eines einzelnen HTML-Tags funktioniert aufgrund der gierigen Natur von .* nicht.
>>> s = '<html><head><title>Title</title>'
>>> len(s)
32
>>> print(re.match('<.*>', s).span())
(0, 32)
>>> print(re.match('<.*>', s).group())
<html><head><title>Title</title>
Der RE gleicht das '<' in '<html>' ab, und das .* verbraucht den Rest des Strings. Es gibt jedoch noch mehr im RE, und das > kann am Ende des Strings nicht übereinstimmen, so dass die reguläre Ausdrucks-Engine Zeichen für Zeichen zurückverfolgen muss, bis sie eine Übereinstimmung für das > findet. Die endgültige Übereinstimmung reicht vom '<' in '<html>' bis zum '>' in '</title>', was nicht das ist, was Sie wollen.
In diesem Fall besteht die Lösung darin, die nicht-gierigen Quantifizierer *?, +?, ?? oder {m,n}? zu verwenden, die so wenig Text wie möglich abgleichen. Im obigen Beispiel wird das '>' sofort nach der ersten '<' Übereinstimmung versucht, und wenn es fehlschlägt, rückt die Engine Zeichen für Zeichen vor und versucht bei jedem Schritt erneut das '>'. Dies führt zum richtigen Ergebnis
>>> print(re.match('<.*?>', s).group())
<html>
(Beachten Sie, dass das Parsen von HTML oder XML mit regulären Ausdrücken mühsam ist. Schnelle und einfache Muster bewältigen gängige Fälle, aber HTML und XML haben Sonderfälle, die die offensichtlichen regulären Ausdrücke brechen; bis Sie einen regulären Ausdruck geschrieben haben, der alle möglichen Fälle behandelt, werden die Muster sehr kompliziert sein. Verwenden Sie für solche Aufgaben ein HTML- oder XML-Parser-Modul.)
Verwendung von re.VERBOSE¶
Sie haben inzwischen wahrscheinlich bemerkt, dass reguläre Ausdrücke eine sehr kompakte Notation sind, aber sie sind nicht besonders lesbar. REs moderater Komplexität können zu langen Sammlungen von Backslashes, Klammern und Metazeichen werden, was sie schwer lesbar und verständlich macht.
Für solche REs kann die Angabe des re.VERBOSE-Flags beim Kompilieren des regulären Ausdrucks hilfreich sein, da es Ihnen ermöglicht, den regulären Ausdruck klarer zu formatieren.
Das Flag re.VERBOSE hat mehrere Auswirkungen. Leerzeichen im regulären Ausdruck, die sich *nicht* in einer Zeichenklasse befinden, werden ignoriert. Das bedeutet, dass ein Ausdruck wie dog | cat äquivalent zu dem weniger lesbaren dog|cat ist, aber [a b] gleicht immer noch die Zeichen 'a', 'b' oder ein Leerzeichen ab. Darüber hinaus können Sie auch Kommentare in einem RE hinterlassen; Kommentare reichen von einem #-Zeichen bis zum nächsten Zeilenumbruch. Wenn dies mit dreifach Anführungszeichen-Strings verwendet wird, können REs sauberer formatiert werden
pat = re.compile(r"""
\s* # Skip leading whitespace
(?P<header>[^:]+) # Header name
\s* : # Whitespace, and a colon
(?P<value>.*?) # The header's value -- *? used to
# lose the following trailing whitespace
\s*$ # Trailing whitespace to end-of-line
""", re.VERBOSE)
Dies ist weitaus besser lesbar als
pat = re.compile(r"\s*(?P<header>[^:]+)\s*:(?P<value>.*?)\s*$")
Feedback¶
Reguläre Ausdrücke sind ein kompliziertes Thema. Hat dieses Dokument Ihnen geholfen, sie zu verstehen? Gab es Teile, die unklar waren, oder Probleme, auf die Sie gestoßen sind, die hier nicht behandelt wurden? Wenn ja, senden Sie bitte Verbesserungsvorschläge an den Autor.
Das vollständigste Buch über reguläre Ausdrücke ist mit ziemlicher Sicherheit Jeffrey Friedl's Mastering Regular Expressions, veröffentlicht von O'Reilly. Leider konzentriert es sich ausschließlich auf die Perl- und Java-Flavours von regulären Ausdrücken und enthält keinerlei Python-Material, sodass es als Referenz für die Programmierung in Python nicht nützlich ist. (Die erste Auflage behandelte Pythons inzwischen entferntes regex-Modul, was Ihnen nicht viel helfen wird.) Erwägen Sie, es aus Ihrer Bibliothek auszuleihen.