Reguläre Ausdrücke sind eine leistungsstarke Sprache zum Abgleichen von Textmustern. Auf dieser Seite erhalten Sie eine grundlegende Einführung in reguläre Ausdrücke selbst, die für unsere Python-Übungen ausreichen, und erfahren, wie reguläre Ausdrücke in Python funktionieren. Das Python-„re“ -Modul unterstützt reguläre Ausdrücke.
In Python wird eine Suche mit regulären Ausdrücken normalerweise so geschrieben:
match = re.search(pat, str)
Die Methode „re.search()“ sucht anhand eines Musters eines regulären Ausdrucks und eines Strings innerhalb des Strings nach diesem Muster. Wenn die Suche erfolgreich ist, gibt „search()“ ein Übereinstimmungsobjekt oder andernfalls „None“ zurück. Daher folgt der Suche normalerweise unmittelbar eine if-Anweisung, um zu testen, ob die Suche erfolgreich war, wie im folgenden Beispiel gezeigt, in dem nach dem Muster „word:“ gesucht wird. gefolgt von einem Wort aus drei Buchstaben (Details siehe unten):
import re str = 'an example word:cat!!' match = re.search(r'word:\w\w\w', str) # If-statement after search() tests if it succeeded if match: print('found', match.group()) ## 'found word:cat' else: print('did not find')
Mit dem Code match = re.search(pat, str)
wird das Suchergebnis in einer Variablen namens „match“ gespeichert. Dann testet die if-Anweisung die Übereinstimmung. Ist wahr, war die Suche erfolgreich und match.group() ist der übereinstimmende Text (z. B. "word:cat"). Wenn die Übereinstimmung „false“ ist (also „None“, um genauer zu sein), war die Suche nicht erfolgreich und es gibt keinen übereinstimmenden Text.
Das „r“ am Anfang des Musterstrings steht für eine Python-"raw", Zeichenfolge, die umgekehrte Schrägstriche ohne Änderung durchläuft. Dies ist sehr praktisch für reguläre Ausdrücke (Java benötigt diese Funktion dringend!). Ich empfehle, Musterzeichenfolgen immer mit dem als Gewohnheit.
Grundmuster
Der Vorteil regulärer Ausdrücke besteht darin, dass sie nicht nur feste Zeichen, sondern auch Muster vorgeben können. Dies sind die einfachsten Muster, die mit einzelnen Zeichen übereinstimmen:
- a, X, 9, < -- Gewöhnliche Zeichen entsprechen genau sich. Folgende Metazeichen stimmen nicht überein, da sie eine besondere Bedeutung haben: . ^ $ * + ? { [ ] \ | ( ) (Details siehe unten)
- . (ein Punkt) – entspricht jedem einzelnen Zeichen mit Ausnahme des Zeilenumbruchs '\n'
- \w -- (kleines w) entspricht einem "Wort" Zeichen: ein Buchstabe oder eine Ziffer oder Unterstriche [a-zA-Z0-9_]. Obwohl "Wort" ist dies eine Gedächtnisstütze: Es wird nur mit einem einzelnen Wort (Zeichen) abgeglichen, nicht mit einem ganzen Wort. \W (großes W) entspricht jedem Zeichen, das kein Wortzeichen ist.
- \b – Grenze zwischen Wort und Nicht-Wort
- \s -- (kleingeschriebenes s) entspricht einem einzelnen Leerzeichen --space, newline, Return, tab, form [ \n\r\t\f]. \S (großes S) entspricht jedem Zeichen, das kein Leerzeichen ist.
- \t, \n, \r -- Tabulator, Zeilenumbruch, Rückgabe
- \d - Dezimalzahl [0-9] (einige ältere Dienstprogramme für reguläre Ausdrücke unterstützen \d nicht, aber sie unterstützen alle \w und \s)
- ^ = start, $ = end - entspricht Anfang oder Ende der Zeichenfolge
- \ -- hemmen die "Besonderheit" einer Figur. Verwenden Sie also zum Beispiel \. für einen Punkt oder \\ für einen Schrägstrich. Wenn Sie nicht sicher sind, ob ein Zeichen eine besondere Bedeutung hat, z. B. '@', können Sie versuchen, einen Schrägstrich davor zu setzen, \@. Wenn dies keine gültige Escape-Sequenz wie \c ist, wird Ihr Python-Programm mit einem Fehler angehalten.
Einfache Beispiele
Witze: Wie nennt man ein Schwein mit drei Augen? piiig!
Die grundlegenden Regeln der Suche mit regulären Ausdrücken nach einem Muster innerhalb eines Strings lauten:
- Die Suche geht von Anfang bis Ende durch den String und wird bei der ersten gefundenen Übereinstimmung beendet.
- Es müssen alle Muster übereinstimmen, aber nicht alle Strings
- Wenn
match = re.search(pat, str)
erfolgreich ist, ist „match“ nicht „None“ und insbesondere „match.group()“ ist der passende Text
## Search for pattern 'iii' in string 'piiig'. ## All of the pattern must match, but it may appear anywhere. ## On success, match.group() is matched text. match = re.search(r'iii', 'piiig') # found, match.group() == "iii" match = re.search(r'igs', 'piiig') # not found, match == None ## . = any char but \n match = re.search(r'..g', 'piiig') # found, match.group() == "iig" ## \d = digit char, \w = word char match = re.search(r'\d\d\d', 'p123g') # found, match.group() == "123" match = re.search(r'\w\w\w', '@@abcd!!') # found, match.group() == "abc"
Wiederholung
Die Dinge werden interessanter, wenn Sie mit + und * Wiederholungen im Muster angeben.
- + -- 1 oder mehr Vorkommen des Musters links davon, z.B. 'i+' = ein oder mehrere is
- * -- 0 oder mehr Vorkommen des Musters links davon
- ? -- Findet 0 oder 1 Vorkommen des Musters links davon
Links ganz links Größte
Zunächst findet die Suche die Übereinstimmung ganz links für das Muster und dann wird versucht, so viel von der Zeichenfolge wie möglich zu verwenden, d.h. + und * gehen so weit wie möglich (die + und * werden als "gierig" gesagt).
Beispiele für Wiederholungen
## i+ = one or more i's, as many as possible. match = re.search(r'pi+', 'piiig') # found, match.group() == "piii" ## Finds the first/leftmost solution, and within it drives the + ## as far as possible (aka 'leftmost and largest'). ## In this example, note that it does not get to the second set of i's. match = re.search(r'i+', 'piigiiii') # found, match.group() == "ii" ## \s* = zero or more whitespace chars ## Here look for 3 digits, possibly separated by whitespace. match = re.search(r'\d\s*\d\s*\d', 'xx1 2 3xx') # found, match.group() == "1 2 3" match = re.search(r'\d\s*\d\s*\d', 'xx12 3xx') # found, match.group() == "12 3" match = re.search(r'\d\s*\d\s*\d', 'xx123xx') # found, match.group() == "123" ## ^ = matches the start of string, so this fails: match = re.search(r'^b\w+', 'foobar') # not found, match == None ## but without the ^ it succeeds: match = re.search(r'b\w+', 'foobar') # found, match.group() == "bar"
Beispiel für E-Mails
Angenommen, Sie möchten die E-Mail-Adresse innerhalb der Zeichenfolge "xyz alice-b@google.com lila Affe" ermitteln. Wir verwenden dies als laufendes Beispiel, um weitere Funktionen für reguläre Ausdrücke zu demonstrieren. Hier ist ein Versuch mit dem Muster r'\w+@\w+':
str = 'purple alice-b@google.com monkey dishwasher' match = re.search(r'\w+@\w+', str) if match: print(match.group()) ## 'b@google'
Bei der Suche wird in diesem Fall nicht die gesamte E-Mail-Adresse zurückgegeben, da das \w nicht mit dem '-' übereinstimmt. oder "." in die Adresse ein. Dieses Problem wird mithilfe der unten aufgeführten Funktionen für reguläre Ausdrücke behoben.
Eckige Klammern
Eckige Klammern können verwendet werden, um eine Reihe von Zeichen zu kennzeichnen, sodass [abc] mit „a“ übereinstimmt. oder „b“ oder „c“. Die Codes \w, \s usw. funktionieren auch in eckigen Klammern, mit der Ausnahme, dass Punkt (.) nur einen literalen Punkt bedeutet. Bei E-Mail-Problemen können Sie mithilfe der eckigen Klammern "." und '-' bis zur Gruppe von Zeichen um das @ mit dem Muster r'[\w.-]+@[\w.-]+' um die gesamte E-Mail-Adresse abzurufen:
match = re.search(r'[\w.-]+@[\w.-]+', str) if match: print(match.group()) ## 'alice-b@google.com'
Gruppenextraktion
Die „Gruppe“ eines regulären Ausdrucks können Sie Teile des übereinstimmenden Textes auswählen. Nehmen wir an, für das E-Mail-Problem möchten wir den Nutzernamen und den Host separat extrahieren. Setzen Sie dazu den Nutzernamen und Host im Muster in Klammern ( ), wie in folgendem Beispiel gezeigt: r'([\w.-]+)@([\w.-]+)'. In diesem Fall ändern die Klammern nicht, was mit dem Muster übereinstimmt; sie bilden stattdessen logische „Gruppen“ in den übereinstimmenden Text ein. Bei einer erfolgreichen Suche ist „match.group(1)“ der Übereinstimmungstext für die erste Klammer links und „match.group(2)“ für den Text der zweiten Klammer links. Bei der einfachen Methode „match.group()“ handelt es sich weiterhin um den gesamten Übereinstimmungstext.
str = 'purple alice-b@google.com monkey dishwasher' match = re.search(r'([\w.-]+)@([\w.-]+)', str) if match: print(match.group()) ## 'alice-b@google.com' (the whole match) print(match.group(1)) ## 'alice-b' (the username, group 1) print(match.group(2)) ## 'google.com' (the host, group 2)
Ein gängiger Workflow bei regulären Ausdrücken besteht darin, dass Sie für das Gesuchte ein Muster schreiben und Klammern hinzufügen, um die gewünschten Teile zu extrahieren.
Findall
findall() ist wahrscheinlich die leistungsstärkste Funktion im re-Modul. Oben haben wir „re.search()“ verwendet, um die erste Übereinstimmung für ein Muster zu finden. findall() sucht *alle* Übereinstimmungen und gibt sie als Liste von Zeichenfolgen zurück, wobei jeder String eine Übereinstimmung darstellt.## Suppose we have a text with many email addresses str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher' ## Here re.findall() returns a list of all the found email strings emails = re.findall(r'[\w\.-]+@[\w\.-]+', str) ## ['alice@google.com', 'bob@abc.com'] for email in emails: # do something with each found email string print(email)
Findall Mit Dateien
Bei Dateien haben Sie vielleicht die Gewohnheit, eine Schleife zu schreiben, um über die Zeilen der Datei zu iterieren. Dann könnten Sie findall() für jede Zeile aufrufen. Lassen Sie stattdessen findall() die Iteration für Sie durchführen – viel besser! Geben Sie einfach den gesamten Dateitext in findall() ein und lassen Sie eine Liste aller Übereinstimmungen in einem einzigen Schritt zurückgeben (wie bereits erwähnt, gibt f.read() den gesamten Text einer Datei in einer einzigen Zeichenfolge zurück):
# Open file f = open('test.txt', encoding='utf-8') # Feed the file text into findall(); it returns a list of all the found strings strings = re.findall(r'some pattern', f.read())
findall und Groups
Der Gruppenmechanismus in Klammern ( ) kann mit findall() kombiniert werden. Wenn das Muster zwei oder mehr Klammern enthält, gibt „findall()“ nicht eine Liste von Zeichenfolgen zurück, sondern eine Liste mit *Tupeln*. Jedes Tupel stellt eine Übereinstimmung des Musters dar und innerhalb des Tupels befinden sich die Daten der Gruppe(1) und der Gruppe(2). Wenn also zwei Klammerngruppen zum E-Mail-Muster hinzugefügt werden, gibt findall() eine Liste von Tupeln zurück, wobei jede Länge 2 den Nutzernamen und den Host enthält, z.B. ('alice', 'google.com').
str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher' tuples = re.findall(r'([\w\.-]+)@([\w\.-]+)', str) print(tuples) ## [('alice', 'google.com'), ('bob', 'abc.com')] for tuple in tuples: print(tuple[0]) ## username print(tuple[1]) ## host
Sobald Sie die Liste der Tupel haben, können Sie sie durchgehen, um für jedes Tupel eine Berechnung durchzuführen. Wenn das Muster keine Klammern enthält, gibt findall() eine Liste gefundener Zeichenfolgen wie in früheren Beispielen zurück. Wenn das Muster ein einzelnes Set von Klammern enthält, gibt findall() eine Liste von Zeichenfolgen zurück, die dieser einzelnen Gruppe entsprechen. (Alternative optionale Funktion: In einigen Fällen sind runde Klammern ( ) im Muster enthalten, die Sie jedoch nicht extrahieren möchten. Schreiben Sie in diesem Fall die Klammern am Anfang mit einem ?:, z.B. (?: ) und diese linke Klammer wird nicht als Gruppenergebnis gezählt.)
RE-Workflow und -Fehlerbehebung
Muster für reguläre Ausdrücke erfüllen in wenigen Zeichen eine Menge Bedeutung , sind jedoch so dicht, dass Sie viel Zeit für die Fehlerbehebung in Ihren Mustern aufwenden können. Richten Sie die Laufzeit so ein, dass Sie ein Muster ausführen und einfach übereinstimmende Inhalte ausgeben können, z. B. indem Sie es mit einem kleinen Testtext ausführen und das Ergebnis von findall() ausgeben. Wenn keine Übereinstimmung gefunden wird, versuchen Sie, das Muster zu schwächen und Teile davon zu entfernen, um zu viele Übereinstimmungen zu erhalten. Wenn es keine Übereinstimmung gibt, können Sie keine Fortschritte machen, da nichts Konkretes zu sehen ist. Sobald die Kampagne zu viele Übereinstimmungen hat, können Sie sie schrittweise verbessern, um genau das zu erreichen, was Sie erreichen möchten.
Optionen
Mit den re-Funktionen können Sie das Verhalten der Musterübereinstimmung ändern. Das Options-Flag wird als zusätzliches Argument zu search() oder findall() hinzugefügt, z. B. re.search(pat; str; re.IGNORECASE).
- IGNORECASE -- ignoriert Groß-/Kleinschreibungunterschiede für den Abgleich, sodass "a" stimmt mit beiden Buchstaben "a" überein und A.
- DOTALL: Punkt (.) darf mit Zeilenumbruch übereinstimmen. Normalerweise entspricht er alles außer dem Zeilenumbruch. Dies kann Sie in die Irre führen, denn .* stimmt wahrscheinlich mit allem überein, aber standardmäßig läuft es nicht über das Ende einer Zeile hinaus. Beachten Sie, dass \s (Leerraum) Zeilenumbrüche beinhaltet. Wenn Sie also eine Reihe von Leerzeichen abgleichen möchten, die eine neue Zeile enthalten können, können Sie einfach \s* verwenden.
- MULTILINE: Innerhalb einer Zeichenfolge, die aus vielen Zeilen besteht, können ^ und $ mit dem Anfang und dem Ende jeder Zeile übereinstimmen. Normalerweise würde ^/$ nur mit dem Anfang und dem Ende der gesamten Zeichenfolge übereinstimmen.
Gierig oder nicht gierig (optional)
Dieser optionale Abschnitt zeigt eine erweiterte Technik für reguläre Ausdrücke, die für die Übungen nicht benötigt wird.
Angenommen, Sie haben Text mit Tags: <b>foo</b> und <i>so weiter</i>
Angenommen, Sie versuchen, jedes Tag mit dem Muster '(<.*>)' abzugleichen. - welche Übereinstimmungen haben sie zuerst?
Das Ergebnis ist ein wenig überraschend, aber der gierige Aspekt des .* führt dazu, dass es mit dem ganzen <b>foo</b> übereinstimmt. und <i>so weiter</i>' als ein großes Spiel. Das Problem ist, dass die .* so weit wie möglich geht, anstatt beim ersten > anzuhalten. (oder „gierig“).
Es gibt eine Erweiterung des regulären Ausdrucks, der ein Fragezeichen (?) am Ende, z. B. .*? oder .+? ändern, sodass sie nicht gierig sind. Jetzt hören sie so schnell wie möglich auf. Das Muster '(<.*?>)' nur <b> als erste Übereinstimmung und '</b>' als zweite Übereinstimmung verwenden usw. abwechselnd koppeln. Üblicherweise wird ein .*? gefolgt von einer konkreten Markierung (in diesem Fall >), an welche die .*? ausgeführt werden muss.
Das *? -Erweiterung, die aus Perl stammt, und reguläre Ausdrücke, die Perl-Erweiterungen enthalten, werden als Perl-kompatible reguläre Ausdrücke bezeichnet. Python unterstützt PCRE. Viele Befehlszeilen-Dienstprogramme usw. haben ein Flag, mit dem sie PCRE-Muster akzeptieren.
Eine ältere, aber weitverbreitete Technik, um die Idee von "all of this characters außer stop at X" zu codieren werden eckige Klammern verwendet. Für das obige Beispiel könnten Sie das Muster schreiben, aber statt .* zum Abrufen aller Zeichen verwenden Sie [^>]*, wodurch alle Zeichen übersprungen werden, die nicht > sind. (Das vorangestellte ^ "umkehrt" die eckige Klammer und entspricht somit jedem Zeichen, das nicht in den Klammern steht.)
Ersetzung (optional)
Mit der Funktion re.sub(pat,Replacement, str) wird nach allen Instanzen von „pattern“ im gegebenen String gesucht und sie ersetzt. Der Ersatzstring kann „\1“, „\2“ enthalten die sich auf den Text aus Gruppe(1), Gruppe(2) usw. des ursprünglichen übereinstimmenden Textes beziehen.
Im folgenden Beispiel wird nach allen E-Mail-Adressen gesucht und sie so geändert, dass der Nutzer (\1) erhalten bleibt, aber yo-yo-dyne.com als Host festgelegt ist.
str = 'purple alice@google.com, blah monkey bob@abc.com blah dishwasher' ## re.sub(pat, replacement, str) -- returns new string with all replacements, ## \1 is group(1), \2 group(2) in the replacement print(re.sub(r'([\w\.-]+)@([\w\.-]+)', r'\1@yo-yo-dyne.com', str)) ## purple alice@yo-yo-dyne.com, blah monkey bob@yo-yo-dyne.com blah dishwasher
Übung
Wenn Sie reguläre Ausdrücke üben möchten, sehen Sie sich die Übung zu Babynamen an.