Python RegEx
Python reguläre Ausdrücke: Syntax, Sondersequenzen, Gruppen, Lookaheads, Flags und re-Modul-Funktionen mit klaren Beispielen.
Reguläre Ausdrücke (Regex) ermöglichen es, Text anhand flexibler Muster statt exakter Zeichenketten zu suchen, zu extrahieren und zu ersetzen. Pythons eingebautes re-Modul stellt alles Notwendige bereit. Dieses Kapitel behandelt das vollständige Regex-Werkzeugset: Syntax, Sondersequenzen, Quantoren, Gruppen, Lookahead/Lookbehind, Flags und alle wesentlichen re-Funktionen — mit korrekten, ausführbaren Beispielen.
Warum Reguläre Ausdrücke verwenden?
Einfache string-Methoden (str.find(), str.replace(), str.split()) funktionieren gut für festen Text. Reguläre Ausdrücke glänzen, wenn das Muster variiert:
- Prüfen, ob eine Zeichenkette wie eine E-Mail-Adresse oder Telefonnummer aussieht.
- Alle Datumsangaben aus einem Dokument extrahieren, unabhängig vom genauen Format.
- HTML-Tags aus einer Zeichenkette entfernen.
- Mehrere verschiedene Leerraumsequenzen durch ein einzelnes Leerzeichen ersetzen.
Wenn es darum geht, eine Form zu beschreiben statt einen festen Wert, greift man zu re.
Raw Strings
Regex-Muster werden fast immer als Raw Strings (r'...') geschrieben. Raw Strings behandeln Backslashes als literale Zeichen, was wichtig ist, da Regex viele Backslash-Sequenzen verwendet (\d, \w, \s, \b). Ohne das r-Präfix wären überall doppelte Backslashes nötig:
import re
# Both patterns are identical — raw string is easier to read
re.findall(r'\d+', 'abc 123') # raw: r'\d+'
re.findall('\\d+', 'abc 123') # normal: '\\d+'Verwenden Sie Raw Strings für jedes Regex-Muster — das ist die universelle Konvention.
Grundlegende Syntax: Metazeichen
Metazeichen sind Zeichen mit besonderer Bedeutung innerhalb eines Musters. Literale Zeichen stimmen genau mit sich selbst überein.
| Metazeichen | Bedeutung |
|---|---|
. | Beliebiges Zeichen außer einem Zeilenumbruch |
^ | Anfang der Zeichenkette (oder Zeile im MULTILINE-Modus) |
$ | Ende der Zeichenkette (oder Zeile im MULTILINE-Modus) |
* | Null oder mehr des vorangehenden Elements |
+ | Eines oder mehr des vorangehenden Elements |
? | Null oder eines des vorangehenden Elements |
{n} | Genau n Wiederholungen |
{n,m} | Zwischen n und m Wiederholungen |
[...] | Zeichenklasse — eines der aufgelisteten Zeichen |
[^...] | Negierte Klasse — jedes Zeichen, das nicht aufgelistet ist |
| | Alternation — entweder der linke oder rechte Ausdruck |
() | Erfassungsgruppe |
\ | Metazeichen maskieren oder Sondersequenz einleiten |
Um ein literales Metazeichen wie . oder * zu treffen, maskieren Sie es mit einem Backslash: \. trifft einen echten Punkt.
Sondersequenzen
Sondersequenzen sind Kurzschreibweisen für Zeichenklassen, die in realen Mustern häufig vorkommen.
| Sequenz | Trifft auf |
|---|---|
\d | Jede Ziffer — entspricht [0-9] |
\D | Jede Nicht-Ziffer |
\w | Wortzeichen: Buchstaben, Ziffern, Unterstrich |
\W | Nicht-Wortzeichen |
\s | Leerzeichen: Leerzeichen, Tabulator, Zeilenumbruch |
\S | Kein Leerzeichen |
\b | Wortgrenze (nullbreite Assertion) |
\B | Keine Wortgrenze |
import re
print(re.findall(r'\d+', 'I have 3 cats and 12 dogs'))
# ['3', '12']
print(re.findall(r'\w+', 'hello_world 123'))
# ['hello_world', '123']
print(re.findall(r'\bPython\b', 'Python Pythonista Python3'))
# ['Python'] — word boundary prevents partial matches\b ist besonders nützlich: Es trifft die Position zwischen einem Wortzeichen und einem Nicht-Wortzeichen, sodass \bPython\b das eigenständige Wort „Python" trifft, nicht jedoch „Pythonista" oder „Python3".
Quantoren
Quantoren steuern, wie oft das vorangehende Element übereinstimmen muss.
import re
print(re.findall(r'a*', 'baaa')) # ['', 'aaa', '']
print(re.findall(r'a+', 'baaa')) # ['aaa']
print(re.findall(r'a?', 'baaa')) # ['', 'a', 'a', 'a', '']
print(re.findall(r'a{3}', 'baaa')) # ['aaa']Gieriges vs. faules Matching
Standardmäßig sind Quantoren gierig — sie treffen so viel Text wie möglich. Durch Anhängen von ? nach dem Quantor wird er faul (trifft so wenig wie möglich).
import re
html = '<b>bold</b> and <i>italic</i>'
print(re.findall(r'<.*>', html))
# ['<b>bold</b> and <i>italic</i>'] — greedy: matches from first < to last >
print(re.findall(r'<.*?>', html))
# ['<b>', '</b>', '<i>', '</i>'] — lazy: matches each individual tagFaule Quantoren sind unerlässlich beim Parsen strukturierten Texts wie HTML- oder JSON-Fragmente.
Zeichenklassen
Eine Zeichenklasse [...] trifft jedes einzelne Zeichen aus der aufgelisteten Menge. Verwenden Sie - für Bereiche und ^ am Anfang, um die Klasse zu negieren.
import re
print(re.findall(r'[aeiou]', 'hello world'))
# ['e', 'o', 'o']
print(re.findall(r'[0-9]', 'a1b2c3'))
# ['1', '2', '3']
print(re.findall(r'[^aeiou\s]+', 'hello world'))
# ['h', 'll', 'w', 'rld'] — consonants only (not vowels, not spaces)Häufige vordefinierte Bereiche: [a-z] Kleinbuchstaben, [A-Z] Großbuchstaben, [0-9] Ziffern, [a-zA-Z0-9] alphanumerisch.
Anker
Anker verbrauchen keine Zeichen — sie behaupten eine Position in der Zeichenkette.
import re
print(re.findall(r'^Python', 'Python is great'))
# ['Python'] — matches only if 'Python' is at the start
print(re.findall(r'great$', 'Python is great'))
# ['great'] — matches only if 'great' is at the end
print(re.findall(r'^Python', 'Learn Python'))
# [] — 'Python' is not at the start of this stringInformationen zum re.MULTILINE-Flag, mit dem ^ und $ zeilenweise statt zeichenkettenweise angewendet werden, finden Sie später in diesem Kapitel.
Alternation
Das Pipe-Zeichen | wirkt wie ein logisches ODER zwischen zwei Ausdrücken.
import re
print(re.findall(r'cat|dog', 'I have a cat and a dog'))
# ['cat', 'dog']
print(re.findall(r'colou?r|colour', 'color and colour'))
# ['color', 'colour']Erfassungsgruppen
Klammern () erstellen eine Erfassungsgruppe. Gruppen ermöglichen das Extrahieren von Teilübereinstimmungen. re.search() gibt ein Match-Objekt zurück; rufen Sie darauf .group(n) oder .groups() auf.
import re
match = re.search(r'(\d{4})-(\d{2})-(\d{2})', '2023-10-05')
if match:
print(match.group(0)) # '2023-10-05' — entire match
print(match.group(1)) # '2023'
print(match.groups()) # ('2023', '10', '05')Benannte Gruppen
Benennen Sie eine Gruppe mit (?P<name>...), um statt über die Position über den Namen auf sie zuzugreifen. Das macht Muster viel einfacher zu pflegen.
import re
match = re.search(
r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})',
'2023-10-05'
)
if match:
print(match.group('year')) # '2023'
print(match.groupdict()) # {'year': '2023', 'month': '10', 'day': '05'}Nicht-erfassende Gruppen
Verwenden Sie (?:...), wenn Sie gruppieren, aber den Wert nicht erfassen möchten.
import re
print(re.findall(r'(?:Mr|Mrs|Ms)\.? \w+', 'Mr. Smith and Mrs. Jones'))
# ['Mr. Smith', 'Mrs. Jones']Lookahead und Lookbehind
Lookahead ((?=...)) und Lookbehind ((?<=...)) behaupten, dass etwas nach oder vor der Übereinstimmung folgt, ohne es in das Ergebnis einzuschließen. Sie sind nullbreit: Sie verbrauchen keine Zeichen.
import re
# Positive lookahead — find numbers followed by 'dollars'
print(re.findall(r'\d+(?= dollars)', '100 dollars and 200 euros'))
# ['100']
# Negative lookahead — find numbers NOT followed by 'dollars'
print(re.findall(r'\b\d+\b(?! dollars)', '100 dollars and 200 euros'))
# ['200']
# Positive lookbehind — extract domain from email addresses
emails = 'Contact [email protected] or [email protected]'
print(re.findall(r'(?<=@)\w+\.\w+', emails))
# ['example.com', 'test.org']Lookbehind-Muster müssen in Python eine feste Breite haben — * oder + dürfen darin nicht verwendet werden.
Das re-Modul: Kernfunktionen
re.search() — Erste Übereinstimmung finden
Gibt ein Match-Objekt für die erste Stelle zurück, an der das Muster übereinstimmt, oder None, wenn keine Übereinstimmung gefunden wird.
import re
text = 'apple banana apple'
match = re.search(r'banana', text)
if match:
print(match.group()) # 'banana'
print(match.span()) # (6, 12)re.match() — Nur am Anfang treffen
Wie re.search(), aber das Muster muss am Anfang der Zeichenkette übereinstimmen.
import re
print(bool(re.match(r'\d+', 'abc123'))) # False — no digit at start
print(bool(re.search(r'\d+', 'abc123'))) # True — digit found anywhereVerwenden Sie re.search(), wenn Sie ein Muster irgendwo suchen möchten; verwenden Sie re.match(), wenn das Muster am Anfang stehen muss.
re.fullmatch() — Gesamte Zeichenkette treffen
Das Muster muss die gesamte Zeichenkette von Anfang bis Ende treffen.
import re
print(bool(re.fullmatch(r'\d{5}', '12345'))) # True — exactly 5 digits
print(bool(re.fullmatch(r'\d{5}', '123456'))) # False — too long
print(bool(re.fullmatch(r'\d{5}', '1234X'))) # False — non-digit presentre.fullmatch() ist ideal für die Eingabevalidierung (Postleitzahlen, Telefonnummern usw.).
re.findall() — Alle nicht überlappenden Übereinstimmungen
Gibt eine Liste aller Übereinstimmungen zurück. Enthält das Muster Gruppen, wird eine Liste von Tupeln zurückgegeben.
import re
print(re.findall(r'\d+', 'abc 123 def 456'))
# ['123', '456']
# With a group — returns list of group values
print(re.findall(r'(\w+)@(\w+\.\w+)', '[email protected] [email protected]'))
# [('alice', 'example.com'), ('bob', 'test.org')]re.finditer() — Iterator von Match-Objekten
Wie re.findall(), gibt aber Match-Objekte einzeln zurück. Nützlich für große Texte oder wenn Positionsinformationen benötigt werden.
import re
for m in re.finditer(r'\d+', 'abc 123 def 456'):
print(m.group(), m.start(), m.end())
# 123 4 7
# 456 12 15re.sub() — Übereinstimmungen ersetzen
Ersetzt jedes Vorkommen des Musters durch eine Ersetzungszeichenkette oder den Rückgabewert einer Funktion.
import re
text = 'apple banana apple'
print(re.sub(r'apple', 'orange', text))
# 'orange banana orange'
# Limit replacements
print(re.sub(r'apple', 'orange', text, count=1))
# 'orange banana apple'
# Backreferences in replacement — reformat a date
print(re.sub(r'(\d{4})-(\d{2})-(\d{2})', r'\3/\2/\1', '2023-10-05'))
# '05/10/2023're.split() — Nach Muster aufteilen
Teilt die Zeichenkette bei jedem Vorkommen des Musters auf.
import re
print(re.split(r',\s*', 'apple, banana, cherry, date'))
# ['apple', 'banana', 'cherry', 'date']
# Limit the number of splits
print(re.split(r',\s*', 'apple, banana, cherry, date', maxsplit=2))
# ['apple', 'banana', 'cherry, date']Muster kompilieren mit re.compile()
Wenn dasselbe Muster viele Male verwendet wird, kompilieren Sie es einmalig mit re.compile(), um die Leistung zu verbessern. Das resultierende Musterobjekt verfügt über alle gleichen Methoden (findall, search, sub usw.).
import re
# Compile once
digit_pattern = re.compile(r'\d+')
# Reuse many times
print(digit_pattern.findall('abc 123 def 456')) # ['123', '456']
print(digit_pattern.sub('NUM', 'abc 123 def 456')) # 'abc NUM def NUM'
print(digit_pattern.search('xyz 99')) # <re.Match object ...>Die Kompilierung ist besonders wertvoll innerhalb von Schleifen oder häufig aufgerufenen Funktionen.
Flags
Flags ändern das Matching-Verhalten. Sie werden als letztes Argument an jede re-Funktion übergeben oder beim Aufruf von re.compile() angegeben. Mehrere Flags können mit | kombiniert werden.
| Flag | Kurzform | Wirkung |
|---|---|---|
re.IGNORECASE | re.I | Groß-/Kleinschreibung ignorieren |
re.MULTILINE | re.M | ^ und $ treffen Anfang/Ende jeder Zeile |
re.DOTALL | re.S | . trifft auch Zeilenumbrüche |
re.VERBOSE | re.X | Leerzeichen und Kommentare im Muster erlauben |
import re
# IGNORECASE
print(re.findall(r'hello', 'Hello HELLO hello', re.IGNORECASE))
# ['Hello', 'HELLO', 'hello']
# MULTILINE — ^ matches the start of each line
text = 'first line\nsecond line\nthird line'
print(re.findall(r'^\w+', text, re.MULTILINE))
# ['first', 'second', 'third']
# DOTALL — . matches newline characters
print(re.findall(r'<.*?>', '<div>\n<p>text</p>\n</div>', re.DOTALL))
# ['<div>', '<p>', '</p>', '</div>']
# VERBOSE — write readable patterns with comments
date_pattern = re.compile(r'''
(?P<year>\d{4}) # four-digit year
-
(?P<month>\d{2}) # two-digit month
-
(?P<day>\d{2}) # two-digit day
''', re.VERBOSE)
print(date_pattern.search('2023-10-05').groupdict())
# {'year': '2023', 'month': '10', 'day': '05'}Benutzereingaben maskieren mit re.escape()
Wenn Sie ein Muster aus vom Benutzer geliefertem Text aufbauen, maskieren Sie diesen immer zuerst, um unbeabsichtigte Metazeichen-Interpretation zu verhindern.
import re
user_input = 'hello.world'
# Without escaping, '.' matches any character
# With escaping, '\.' matches a literal dot
safe_pattern = re.escape(user_input)
print(safe_pattern) # 'hello\\.world'
print(bool(re.search(safe_pattern, 'say hello.world'))) # True
print(bool(re.search(safe_pattern, 'say helloXworld'))) # FalsePraktische Beispiele
E-Mail-Adresse validieren
import re
def is_valid_email(email):
pattern = r'^[\w.+-]+@[\w-]+\.[a-zA-Z]{2,}$'
return bool(re.fullmatch(pattern, email))
print(is_valid_email('[email protected]')) # True
print(is_valid_email('bad-email@')) # FalseAlle URLs aus Text extrahieren
import re
text = 'Visit https://www.example.com or http://test.org for details.'
urls = re.findall(r'https?://[\w./-]+', text)
print(urls)
# ['https://www.example.com', 'http://test.org']Überflüssige Leerzeichen entfernen
import re
messy = ' hello world Python '
clean = re.sub(r'\s+', ' ', messy).strip()
print(clean) # 'hello world Python'Eine Log-Zeile parsen
import re
log = '2023-10-05 14:32:11 ERROR Failed to connect to database'
pattern = re.compile(
r'(?P<date>\d{4}-\d{2}-\d{2}) '
r'(?P<time>\d{2}:\d{2}:\d{2}) '
r'(?P<level>\w+) '
r'(?P<message>.+)'
)
m = pattern.match(log)
if m:
print(m.group('level')) # 'ERROR'
print(m.group('message')) # 'Failed to connect to database'Häufige Fallstricke
re.match() vs. re.search() — re.match() prüft nur den Anfang der Zeichenkette. Neue Benutzer erwarten oft, dass es überall sucht, und sind verwirrt, wenn None zurückgegeben wird.
Raw Strings vergessen — '\d' in einer normalen Python-Zeichenkette ist einfach 'd' mit einem nicht erkannten Escape-Zeichen (oder eine SyntaxWarning in Python 3.12+). Schreiben Sie immer r'\d'.
Gieriges Matching, das zu viel erfasst — Wenn ein .*-Muster mehr als beabsichtigt erfasst, wechseln Sie zu .*? (faul).
Sonderzeichen in Ersetzungsstrings — In re.sub() verweisen Backslash-Sequenzen wie \1 im Ersetzungsstring auf erfasste Gruppen. Um einen literalen Backslash in der Ersetzung einzufügen, schreiben Sie \\.
re.findall() mit Gruppen — Wenn das Muster Gruppen enthält, gibt re.findall() die Gruppen zurück, nicht die vollständige Übereinstimmung. Verwenden Sie eine nicht-erfassende Gruppe (?:...), wenn Sie die vollständige Übereinstimmung möchten.
Kurzreferenz
| Aufgabe | Funktion |
|---|---|
| Erste Übereinstimmung finden | re.search(pattern, text) |
| Alle Übereinstimmungen finden | re.findall(pattern, text) |
| Übereinstimmungen iterieren | re.finditer(pattern, text) |
| Am Anfang treffen | re.match(pattern, text) |
| Gesamte Zeichenkette treffen | re.fullmatch(pattern, text) |
| Ersetzen | re.sub(pattern, repl, text) |
| Aufteilen | re.split(pattern, text) |
| Für Wiederverwendung kompilieren | re.compile(pattern) |
| Benutzereingabe maskieren | re.escape(text) |
Verwandte Kapitel
- Python Strings — string-Methoden, die Regex bei einfacheren Textaufgaben ergänzen.
- Modify Strings — eingebaute Methoden wie
replace()undsplit(), die Regex vermeiden, wenn Muster fest sind. - Python File Handling — Dateien zeilenweise lesen, um Regex in großem Maßstab anzuwenden.
- Python Try/Except — Fehlerbehandlung bei Regex-Mustern, die aus Benutzereingaben kompiliert werden.
- Python Functions — Regex-Logik in wiederverwendbare Funktionen kapseln.