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
CASE-002
Grep & Co. Datendetektei
Fallakte
Auftraggeber
Kommissar Kang vom zuständigen Revier
Beweismittel
Eine Zeugenaussage und das Ein- und Ausfahrtsprotokoll einer Tiefgarage. Die Zeugin hat das Nummernschild nur teilweise gesehen.
Zum Abschluss des Falls
Zieh alle Nummernschilder aus dem Protokoll und grenz die Auswahl auf das eine Auto ein, das zur Zeugenaussage passt.
Am frühen Abend schiebt sich Kommissar Kang durch die Tür und schüttelt seinen nassen Schirm aus. „Gestern Nacht gegen 22:45 Uhr, Fahrerflucht an der Ausfahrt einer Tiefgarage. Die Zeugin hat das Kennzeichen nicht ganz gesehen.“ Die Chefin nimmt die Hände nicht von der Tastatur. „Nicht ganz gesehen? Dann schreib eben das als Muster auf, was sie gesehen hat. Beweise hinterlassen Muster.“
Die Zeugenaussage, so wie sie im Protokoll steht:
- 앞 두 자리는 12 아니면 17이었어요.
- 가운데 한글은 가, 거, 고 중 하나였어요.
- 마지막 숫자는 7이었어요.
- 차는 출구(OUT) 쪽으로 빠져나갔어요.Übersetzt heißt das:
Koreanische Nummernschilder sehen so aus: zwei Ziffern, ein Hangul-Zeichen (koreanische Silbe), vier Ziffern, etwa 12가3457. Hier das Ein- und Ausfahrtsprotokoll der Tiefgarage. Die Kamera liest die Schilder manchmal falsch.
2026-10-19 22:41 IN B1 12가3457
2026-10-19 22:43 IN B1 34나1207
2026-10-19 22:47 OUT B2 17거3407
2026-10-19 22:52 IN B1 12-3457
2026-10-19 22:58 OUT B2 56다9081
2026-10-19 23:05 IN B1 12a3457Jede Zeile der Aussage ist eine Bedingung der Form „an dieser Stelle steht eins von diesen Zeichen“. Genau dafür gibt es die Zeichenklassen, um die es heute geht.
Eckige Klammern [...] stehen für genau ein beliebiges Zeichen aus der Liste darin. Die Zeugin sagt „eins von 가, 거 oder 고“, also schreibst du für diese Stelle [가거고].
import re
re.findall("12[가거고]3457", "12가3457 12거3457 12나3457") # ['12가3457', '12거3457']
re.findall(r"[ab]c", "ac bc cc") # ['ac', 'bc']
re.search(r"[abc]", "xyz") # None[가거고] sind nicht drei Zeichen, sondern eins.[가거고] und [고거가] sind dieselbe Klasse.. aus Lektion 1 „ein beliebiges Zeichen“, so sind eckige Klammern „eins der ausgewählten Zeichen“.Beweislabor
re.search(r"", text)
Rückgabewert
Alle zehn Ziffern als [0123456789] auszuschreiben ist mühsam. Steht in eckigen Klammern ein - zwischen zwei Zeichen, entsteht ein Bereich. [0-9] heißt 0 bis 9, [a-z] alle lateinischen Kleinbuchstaben.
re.findall(r"[0-9]", "B1") # ['1']
re.findall(r"[a-z0-9]", "B1 b2") # ['1', 'b', '2']
re.findall(r"[a-zA-Z]", "a1Z") # ['a', 'Z'][a-z0-9] ist ein Kleinbuchstabe oder eine Ziffer.[a-z].Die fertig zusammengesetzten Hangul-Silben liegen in Unicode lückenlos hintereinander, von 가 (U+AC00) bis 힣 (U+D7A3), insgesamt 11.172 Zeichen. Ein einziges [가-힣] erfasst also jede vollständige Hangul-Silbe.
print(hex(ord("가")), hex(ord("힣"))) # 0xac00 0xd7a3
re.findall(r"[가-힣]", "ㄱ ㅏ 가") # ['가']Einzelne Laute wie ㄱ oder ㅏ liegen in einem anderen Block und gehören nicht zu [가-힣]. Das mittlere Zeichen eines Nummernschilds ist immer eine vollständige Silbe, dieser Bereich reicht also.
Schreibst du einen Bereich falsch herum, gibt es schon beim Erzeugen des Musters einen Fehler.
re.compile("[a-Z]") # re.PatternError: bad character range a-Z at position 1Beweislabor
re.search(r"", text)
Rückgabewert
Dots Tipp
Bereiche gehen immer vom kleineren zum größeren Codepoint. Wenn du Groß- und Kleinbuchstaben auf einmal willst und [a-Z] schreibst, gibt es einen Fehler. a ist Nummer 97, Z Nummer 90, das ist verkehrt herum. Willst du beides, nimm zwei Bereiche: [a-zA-Z].
Steht ^ ganz vorn direkt hinter der öffnenden Klammer, kehrt sich die Bedeutung um. [^0-9] ist ein Zeichen, das keine Ziffer ist.
re.findall(r"[^0-9]", "12가34") # ['가']
re.findall(r"[0-9^]", "2^3") # ['2', '^', '3']
re.findall(r"[^0-9]", "1\n2") # ['\n']^ verneint nur ganz vorn. Steht es weiter hinten wie in [0-9^], ist es einfach das Zeichen ^.[^0-9] belegt ein Zeichen. Es heißt nicht „eine Stelle ohne Ziffer“, sondern „ein Zeichen, das keine Ziffer ist“.[^0-9] auch darauf.Die Garagenkamera hält Ziffern manchmal für lateinische Buchstaben: 1 für I, 5 für S, 2 für Z. Suchst du nach Zeichen, die weder Ziffer noch Hangul noch Leerzeichen sind, kommen die falsch gelesenen Stellen zum Vorschein.
Dots Tipp
Ich komme nicht übers Fenster (den Zeilenumbruch), aber [^0-9] schon. Ein Zeilenumbruch ist schließlich auch „ein Zeichen, das keine Ziffer ist“. Wenn du [^...] auf mehrzeilige Protokolle loslässt, prüf unbedingt, ob es auch Zeilenumbrüche erwischt.
Beweislabor
re.search(r"", text)
Rückgabewert
Häufig gebrauchte Klassen kürzt man mit einem Backslash und einem Buchstaben ab. Als Großbuchstabe geschrieben, bedeuten sie das Gegenteil.
| Schreibweise | Bedeutung | Etwa dieselbe Klasse wie |
|---|---|---|
\d | eine Ziffer | [0-9] |
\D | ein Zeichen, das keine Ziffer ist | [^0-9] |
\w | ein Buchstabe, eine Ziffer oder ein Unterstrich (auch Hangul) | weiter als [a-zA-Z0-9_] |
\W | ein Zeichen, das kein \w ist | |
\s | ein Leerraumzeichen (Leerzeichen, Tab, Zeilenumbruch …) | weiter als [ \t\n\r\f\v] |
\S | ein Zeichen, das kein Leerraum ist |
re.findall(r"\d", "B1 12가") # ['1', '1', '2']
re.findall(r"\D", "12가-3") # ['가', '-']
re.findall(r"\w", "a_1 가!") # ['a', '_', '1', '가']
re.findall(r"\W", "a_1 가!") # [' ', '!']
re.findall(r"\s", "a b\tc\nd") # [' ', '\t', '\n']
re.findall(r"\S", "a b\tc") # ['a', 'b', 'c']Kurzschreibweisen kannst du in eckigen Klammern mit anderen Zeichen mischen. [\d,.] ist ein Zeichen, das eine Ziffer, ein Komma oder ein Punkt ist.
re.findall(r"\d[\d,.]\d", "1,2 3.4 5-6 789") # ['1,2', '3.4', '789']Schreiben wir das Nummernschild jetzt mit Kurzschreibweisen. Für die Mitte \D („keine Ziffer“) zu nehmen, wirkt bequem, lässt aber auch die falsch gelesenen 12-3457 und 12a3457 durch. In der Mitte muss nicht „irgendein Zeichen außer einer Ziffer“ stehen, sondern „eine Hangul-Silbe“.
Beweislabor
re.search(r"", text)
Rückgabewert
In eckigen Klammern verlieren die meisten Metazeichen ihre Kraft. [.*+?] ist einfach ein Zeichen aus Punkt, Stern, Plus und Fragezeichen. Einen Backslash brauchst du dafür nicht.
Nur vier Zeichen bleiben auch in eckigen Klammern besonders:
\ : Escapes und Kurzschreibweisen wie \d] : das Ende der Klasse. Steht es aber ganz vorn, ist es ein normales Zeichen^ : verneint, wenn es ganz vorn steht- : Bereich. Steht es aber ganz vorn oder ganz hinten, ist es ein normales Zeichenre.findall(r"[.*+?]", "a.b*c+d?") # ['.', '*', '+', '?']
re.findall(r"[]a]", "a]b") # ['a', ']']
re.findall(r"[-+]", "3-2+1") # ['-', '+']
re.findall(r"[a\-z]", "a-mz") # ['a', '-', 'z']
re.findall(r"[\]\\]", r"a]b\c") # [']', '\\'][a\-z] ist kein Bereich, sondern die drei Zeichen a, - und z. Deshalb wird m nicht gefunden. Wenn dich die Positionsregeln verwirren, setz einfach einen Backslash davor. Das liest sich ohnehin leichter.
Ein str-Muster arbeitet in Python standardmäßig mit Unicode. Die Kurzschreibweisen sind darum weiter gefasst, als man denkt. Dass \w Hangul erfasst, hast du schon gesehen. \d hält auch die arabisch-indische Ziffer ٣ (U+0663, die Ziffer 3) für eine Ziffer. Und \s erfasst das vollbreite Leerzeichen (U+3000), das sich gern in ostasiatische Texte mischt.
re.findall(r"\d", "3٣") # ['3', '٣']
print(int("٣")) # 3
re.findall(r"\s", "a\u3000b") # ['\u3000'][0-9] statt \d.re.ASCII, das die Kurzschreibweisen auf den ASCII-Bereich einschränkt, lernst du in Lektion 11.Und zum Schluss: Schreibst du in eckigen Klammern dasselbe Symbol doppelt, also [[, --, &&, ~~ oder ||, gibt es eine FutureWarning. Python hält sich diese Stellen frei, um sie später für Mengenoperationen zwischen Klassen (Vereinigung, Schnittmenge usw.) zu verwenden.
import warnings
warnings.simplefilter("always")
re.compile(r"[[a]") # FutureWarning: Possible nested set at position 1
re.compile(r"[a&&b]") # FutureWarning: Possible set intersection at position 2Heute funktioniert das noch, aber die Bedeutung kann sich in Zukunft ändern. Brauchst du solche Zeichen wirklich, escape eins davon: [\[a] oder [a&\&b]. In Lektion 12 fassen wir das noch einmal zusammen.
Nimm an, das Garagenprotokoll steckt im String parking. Zuerst ziehst du nur die echten Nummernschilder heraus. Die zwei falsch gelesenen Zeilen fallen weg.
plates = re.findall(r"\d\d[가-힣]\d\d\d\d", parking)
print(plates) # ['12가3457', '34나1207', '17거3407', '56다9081']Jetzt übersetzt du die Zeugenaussage Stelle für Stelle in Klassen. Die ersten beiden Ziffern sind 1[27], die Mitte ist [가거고], die letzte Ziffer 7.
print(re.findall(r"1[27][가거고]\d\d\d7", parking)) # ['12가3457', '17거3407']
print(re.findall(r"OUT B\d 1[27][가거고]\d\d\d7", parking)) # ['OUT B2 17거3407']Es gibt zwei Kandidaten, aber 12가3457 ist vor dem Unfall hineingefahren und hat die Garage laut Protokoll nicht verlassen. Zwei Minuten nach dem Unfall ist nur ein Auto zur Ausfahrt hinaus: 17거3407.
Fall abgeschlossen — Das Fahrzeug 17거3407, das um 22:47 über die Ausfahrt B2 die Garage verlassen hat, passt in allen Punkten zur Zeugenaussage. Kommissar Kang wurde um eine Halterabfrage gebeten.
Als Kommissar Kang gegangen ist, tippt die Chefin mit dem Finger auf das Muster auf dem Bildschirm. „\d\d[가-힣]\d\d\d\d. Viermal dasselbe hingeschrieben. Und neuere Kennzeichen haben vorn sogar drei Ziffern.“ In der nächsten Lektion lernst du, dieses lange Muster zu kürzen.
B1 und B2 findet. Worin unterscheiden sich B[12] und B\d?[^\d\s]? Überleg es dir zuerst am Kameraprotokoll von oben, 12가3457 I2가3457 12가34S7 1Z거3407, und führe es dann aus.22:41.Das Notizbuch der Direktorin
[abc] ist ein Zeichen aus a, b, c. Egal wie viele Zeichen drinstehen, es belegt nur eine Stelle.
Bereiche wie [a-z0-9] folgen der Codepoint-Reihenfolge. Hangul-Silben: [가-힣]. [a-Z] ist ein Fehler.
[^...] verneint nur mit ^ ganz vorn. Auch Zeilenumbrüche passen darauf.
\d \w \s und ihr Gegenteil \D \W \S. In Klassen mischbar, etwa [\d,.].
In einer Klasse sind . * + ? ganz normale Zeichen. Besonders sind nur \ ] ^ -.
\d \w \s in str-Mustern umfassen ganz Unicode. Nur 0 bis 9: [0-9].
← Zurück
Lektion 1 · Die verschwundene Zahlung: Text wörtlich finden
Weiter →
Lektion 3 · Das zerrissene Telefonbuch: Quantoren
Ü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.