Reguläre Ausdrücke zum Ansehen
·
Kursüberblick
1
Die verschwundene Zahlung: Text wörtlich finden
2
Der Fluchtwagen: Zeichenklassen
3
Das zerrissene Telefonbuch: Quantoren
4
Die falschen Anmeldungen: Anker und Validierung
5
Daten in jedem Format: Gruppen und Alternativen
6
Der Zugriff um 3 Uhr nachts: benannte Gruppen und Match-Objekte
7
Die Nacht vor der Story: Ersetzen und Aufteilen
8
Die Herausforderung des Backtrackers: gierig, genügsam, possessiv
9
Die Regeln des Tresors: Lookaround
10
Gleiche Masche: Rückverweise und Bedingungen
11
Der unlesbare Regex: Flags und Kompilieren
12
Die Enttarnung des Backtrackers: Rückblick und ein Tokenizer
Zeit 2:30–3:20
CASE-004
Grep & Co. Datendetektei
Fallakte
Auftraggeber
Frau Jeong, Betreiberin des Onlineshops „Markt Jeong“
Beweismittel
Die Liste der Registrierungsanfragen und eine Python-Funktion, die die Regeln für Benutzernamen prüft.
Zum Abschluss des Falls
Fische alle Benutzernamen heraus, die gegen die Regeln verstoßen, und finde heraus, warum die Prüfung versagt hat.
Gegen Mittag kommt Frau Jeong herein, ihren Laptop fest an sich gedrückt. „Ich habe ganz klare Regeln für Benutzernamen festgelegt. Und trotzdem kommen hier die seltsamsten Namen rein.“ Die Chefin schaut lange auf den Bildschirm, dann sagt sie knapp: „Die Regel stimmt. Nur die Frage ist falsch gestellt.“
So sehen die Namensregel von Markt Jeong und die Prüffunktion aus. Die Regel steht auf Koreanisch in der Doku des Shops:
아이디 규칙: 영문 소문자로 시작하고, 소문자·숫자·밑줄(_)만 써서 4~16자Auf Deutsch: Benutzername: beginnt mit einem lateinischen Kleinbuchstaben, besteht nur aus Kleinbuchstaben, Ziffern und Unterstrichen (_), insgesamt 4 bis 16 Zeichen.
import re
ID = r"[a-z][a-z0-9_]{3,15}"
def is_valid_id(uid):
return re.search(ID, uid) is not NoneUnd hier die Registrierungsanfragen:
2026-10-18 14:02:51 signup id=jeong_fan
2026-10-19 03:11:02 signup id=ok_id!!!
2026-10-19 03:11:04 signup id=_admin
2026-10-19 03:11:05 signup id=ABC123
2026-10-19 03:11:07 signup id=a1
2026-10-19 09:40:13 signup id=minsu99Sieh dir zuerst an, was Frau Jeongs Prüfung durchlässt.
print(is_valid_id("ok_id!!!")) # True ← trotz Ausrufezeichen durchgekommen
print(is_valid_id("_admin")) # True ← beginnt mit Unterstrich, trotzdem durch
print(is_valid_id("ABC123")) # Falsere.search fragt: „Gibt es irgendwo in diesem String einen Teil, der zur Regel passt?“ In ok_id!!! steckt ok_id, in _admin steckt admin, also Erfolg.
re.search(ID, "ok_id!!!") # <re.Match object; span=(0, 5), match='ok_id'>
re.search(ID, "!!!ok_id") # <re.Match object; span=(3, 8), match='ok_id'>Eine Eingabeprüfung muss eine andere Frage stellen: „Passt der ganze String zur Regel?“ Dafür muss im Muster stehen: „Hier ist der Anfang, hier ist das Ende.“
Außerhalb eckiger Klammern steht ^ für den Anfang des Strings und $ für das Ende des Strings. Solche Zeichen heißen Anker (anchor).
re.search(r"^id", "id=kim") # <re.Match object; span=(0, 2), match='id'>
re.search(r"^id", "my id=kim") # None
re.search(r"kim$", "id=kim") # <re.Match object; span=(3, 6), match='kim'>
re.search(r"kim$", "id=kim!") # None^id weiterhin aus den zwei Zeichen id.[^...] aus Lektion 2. Ganz vorn in eckigen Klammern verneint ^, außerhalb davon steht es für den Anfang.Setzt du ^ und $ vor und hinter das Muster der Prüfung, muss der passende Teil vom ersten bis zum letzten Zeichen reichen.
Beweislabor
re.search(r"", text)
Rückgabewert
Auch ohne selbst Anker zu schreiben, kannst du über die Funktion wählen, wo verglichen wird.
| Funktion | Fragt | Mit Ankern geschrieben |
|---|---|---|
re.search | Passt irgendwo ein Teil? | (keine Anker) |
re.match | Passt ein Teil ab dem Anfang? | \AMuster |
re.fullmatch | Passt das Ganze? | \AMuster\Z |
\A und \Z sind Anker ähnlich wie ^ und $. Den Unterschied siehst du in 4-4.
re.match(ID, "ok_id!!!") # <re.Match object; span=(0, 5), match='ok_id'>
re.match(ID, "!!!ok_id") # None
re.fullmatch(ID, "ok_id!!!") # None
re.fullmatch(ID, "ok_id") # <re.Match object; span=(0, 5), match='ok_id'>re.match nicht, ob alles passt. Es reicht, wenn der Anfang passt, und so lässt es ok_id!!! durch.re.fullmatch. Es ist sicher, auch wenn du die Anker vergisst, und es umgeht die Falle aus dem nächsten Abschnitt.Beweislabor
re.search(r"", text)
Rückgabewert
$ hat eine versteckte Regel. Es passt nicht nur am echten Ende des Strings, sondern auch direkt vor einem abschließenden Zeilenumbruch. Deshalb lässt ^abc$ auch "abc\n" mit angehängtem Zeilenumbruch durch.
re.search(r"^abc$", "abc\n") # <re.Match object; span=(0, 3), match='abc'>
re.fullmatch(r"abc", "abc\n") # None
re.search(r"\Aabc\Z", "abc\n") # None\A passt nur ganz am Anfang des Strings, \Z nur ganz am Ende. Eine Ausnahme für Zeilenumbrüche gibt es nicht.\z. In Python ist es genau dasselbe wie \Z. Es kam dazu, damit die Schreibweise zu Regexen in anderen Sprachen passt.re.search(r"\Aabc\z", "abc") # <re.Match object; span=(0, 3), match='abc'>
re.search(r"\Aabc\z", "abc\n") # NoneDots Tipp
Zeilen, die du aus einer Datei liest, haben oft noch einen Zeilenumbruch am Ende. Prüfst du mit ^...$, tut das Muster so, als hätte es ihn nicht gesehen, und lässt ihn durch. Dann landet der Benutzername samt Zeilenumbruch in der Datenbank. Zum Prüfen also fullmatch oder \A...\Z.
Da ist noch die Hausaufgabe aus Lektion 3: Das Telefonnummer-Muster hat mitten in 1234-5678-9012 mit 234 angefangen. Diesmal brauchst du keinen Anker für den ganzen String, sondern einen, der in einem langen Text Anfang und Ende einer einzelnen Nummer markiert.
\b ist eine Wortgrenze. Sie passt auf eine Stelle zwischen einem \w (Buchstabe, Ziffer, Unterstrich) und einem \W (jedes andere Zeichen) oder auf Anfang oder Ende des Strings, wenn dort ein \w steht. \B ist das Gegenteil, eine Stelle, die keine Grenze ist. Beide belegen kein Zeichen.
re.findall(r"\bid\b", "id=kim userid=lee id_card") # ['id']
re.findall(r"\Bid\b", "id=kim userid=lee id_card") # ['id']
re.findall(r"\bid\B", "id=kim userid=lee id_card") # ['id']Alle drei Ergebnisse sind ['id'], aber gefunden wurden sie an verschiedenen Stellen.
\bid\b : das id in id=kim, mit einer Grenze davor und dahinter\Bid\b : das id in userid, davor keine Grenze\bid\B : das id in id_card, dahinter ein Unterstrich (\w), also keine GrenzeSetzt du \b vor und hinter das Telefonnummer-Muster, kann es nicht mehr mitten in einer Zahl anfangen. Zwischen 1 und 2 ist keine Grenze, denn beide sind \w.
re.findall(r"\b\d{2,3}[-. ]?\d{3,4}[-. ]?\d{4}\b", phonebook)
# ['010-0000-1234', '010 0000 9876', '02-0000-5678', '010.0000.4321']Eine Sache noch: In Python ist auch Hangul \w. Direkt vor Ziffern, die unmittelbar an Hangul hängen, gibt es also keine Grenze. Im folgenden Beispiel steht 전화 für „Telefon“.
re.findall(r"\b\d{2,3}-\d{3,4}-\d{4}\b", "전화010-0000-1234") # []
re.findall(r"\b\d{2,3}-\d{3,4}-\d{4}\b", "전화 010-0000-1234") # ['010-0000-1234']Beweislabor
re.search(r"", text)
Rückgabewert
Stehen wie bei den Registrierungen mehrere Zeilen in einem String, passt ^ nur am Anfang der ersten Zeile. Willst du das Datum am Anfang jeder Zeile herausziehen, gibst du das Flag re.MULTILINE (kurz re.M) mit. Dann passen ^ und $ am Anfang und Ende jeder Zeile.
re.findall(r"^\d{4}-\d\d-\d\d", signups)
# ['2026-10-18']
re.findall(r"^\d{4}-\d\d-\d\d", signups, re.MULTILINE)
# ['2026-10-18', '2026-10-19', '2026-10-19', '2026-10-19', '2026-10-19', '2026-10-19']Auch mit re.M passen \A und \Z weiterhin nur am Anfang und Ende des ganzen Strings.
re.findall(r"^\d", "1\n2", re.M) # ['1', '2']
re.findall(r"\A\d", "1\n2", re.M) # ['1']Alle Flags behandeln wir ausführlich in Lektion 11. Für heute reicht: Es gibt einen Schalter, der ^ und $ auf einzelne Zeilen umstellt.
Beweislabor
re.search(r"", text)
Flags
Rückgabewert
Ersetz in der Prüfung re.search durch re.fullmatch. Das Muster bleibt, wie es ist.
def is_valid_id(uid):
return re.fullmatch(r"[a-z][a-z0-9_]{3,15}", uid) is not None
for line in signups.splitlines():
uid = line.split("id=")[1]
if not is_valid_id(uid):
print(line)
# 2026-10-19 03:11:02 signup id=ok_id!!!
# 2026-10-19 03:11:04 signup id=_admin
# 2026-10-19 03:11:05 signup id=ABC123
# 2026-10-19 03:11:07 signup id=a1Alle vier aussortierten Anfragen verstoßen gegen die Regeln: ok_id!!! hat Ausrufezeichen, _admin beginnt mit einem Unterstrich, ABC123 hat Großbuchstaben, a1 ist zu kurz.
Fall abgeschlossen — Die Prüfung der Benutzernamen hat mit re.search alles durchgelassen, was „wenigstens teilweise passt“. Nach der Umstellung auf re.fullmatch wurden 4 regelwidrige Anmeldungen blockiert.
Doch die Chefin fährt mit dem Finger die Uhrzeiten der vier Zeilen entlang. 03:11:02, 03:11:04, 03:11:05, 03:11:07. „Viermal in fünf Sekunden. Um drei Uhr nachts. Das war keine Menschenhand.“ Frau Jeongs Gesicht wird starr. Die falschen Konten kamen alle auf einmal, in derselben Nacht. Wer sie geschickt hat, weiß noch niemand.
Damit endet Teil 1. Ein Zeichen, eine Stelle, Anzahlen, Grenzen: Du weißt jetzt, wie man aufschreibt, was man sucht. In Teil 2 lernst du, aus dem Gefundenen die Teile herauszulösen, die du brauchst, und jagst damit den Besucher aus dieser Nacht.
re.match(r"\d{4}", "2026-10-19") und re.match(r"\d{4}", "#2026") ergeben, und probier es aus.re.findall(r"\bcat\b", "cat concat cats") und re.findall(r"\Bcat", "cat concat cats")?Das Notizbuch der Direktorin
^ ist der Anfang, $ das Ende. Anker passen nicht auf Zeichen, sondern auf Positionen.
re.search irgendwo, re.match nur am Anfang, re.fullmatch das Ganze. Eingabeprüfung: fullmatch.
$ passt auch vor einem abschließenden Zeilenumbruch. Das Ende ohne diese Ausnahme: \Z (seit 3.14 auch \z).
\b ist die Grenze zwischen \w und \W, \B eine Stelle ohne Grenze. Auch Hangul ist \w.
Mit re.M passen ^ $ an jeder Zeile. \A \Z beziehen sich weiter auf den ganzen String.
← Zurück
Lektion 3 · Das zerrissene Telefonbuch: Quantoren
Weiter →
Lektion 5 · Daten in jedem Format: Gruppen und Alternativen
Über neue Lektionen informiert werden
Wir schicken dir eine E-Mail, wenn dieser Kurs neue Lektionen bekommt oder ein Folgekurs erscheint.
R
ReflexKR
Python und Reflex zum Anfassen. Alle Beispiele wurden mit Reflex 0.10.0 ausgeführt und geprüft.
ReflexKR ist kostenlos und ohne Werbebanner. Wenn es dir geholfen hat, kannst du es mit einem Kaffee unterstützen. ☕ Spendier uns einen Kaffee
Hinweis zu Affiliate-Links© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0
Fragen & Antworten
Frag alles zu dieser Lektion. Fragen aus allen Sprachversionen dieser Seite erscheinen hier gemeinsam.