R

ReflexKR

Reflex-Dokumentation

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

Reguläre Ausdrücke zum Ansehen
Lektion 2

Zeit 0:50–1:40

Der Fluchtwagen: Zeichenklassen

CASE-002

Grep & Co. Datendetektei

Fallakte

Der Fluchtwagen

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:

  • Die ersten beiden Ziffern waren 12 oder 17.
  • Das Hangul-Zeichen in der Mitte war eins von 가, 거 oder 고.
  • Die letzte Ziffer war eine 7.
  • Das Auto ist Richtung Ausfahrt (OUT) davongefahren.

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 12a3457

Jede 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.

2-1. Eckige Klammern: eins von diesen Zeichen (5 Min.)

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
  • Egal, wie viele Zeichen in den Klammern stehen: Sie belegen eine einzige Stelle. [가거고] sind nicht drei Zeichen, sondern eins.
  • Die Reihenfolge spielt keine Rolle. [가거고] und [고거가] sind dieselbe Klasse.
  • War der . aus Lektion 1 „ein beliebiges Zeichen“, so sind eckige Klammern „eins der ausgewählten Zeichen“.

Beweislabor

re.search(r"", text)

Rückgabewert

Ersetz das mittlere Zeichen durch [가거고]. 가, 거 und 고 müssen durchkommen, 나 muss draußen bleiben.

2-2. Bereiche: [0-9], [a-z], [가-힣] (8 Min.)

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']
  • Du kannst mehrere Bereiche hintereinanderschreiben. [a-z0-9] ist ein Kleinbuchstabe oder eine Ziffer.
  • Maßgeblich für einen Bereich ist die Reihenfolge der Codepoints (Unicode-Nummern) der Zeichen. Deshalb gehören Großbuchstaben nicht zu [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 1

Beweislabor

re.search(r"", text)

Rückgabewert

Ersetz das mittlere '가' durch [가-힣] und finde alle vier Nummernschilder. Die zwei Zeilen, die die Kamera falsch gelesen hat, müssen wegfallen.

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].

2-3. Verneinung: [^...] – alles außer diesen (7 Min.)

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 ^.
  • Auch [^0-9] belegt ein Zeichen. Es heißt nicht „eine Stelle ohne Ziffer“, sondern „ein Zeichen, das keine Ziffer ist“.
  • Ein Zeilenumbruch ist auch keine Ziffer, also passt [^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

[^0-9] erwischt auch Hangul und Leerzeichen. Pack zusätzlich 가-힣 und ein Leerzeichen in die Klammern, damit nur die falsch gelesenen Zeichen übrig bleiben.

2-4. Kurzschreibweisen: \d \w \s (10 Min.)

Häufig gebrauchte Klassen kürzt man mit einem Backslash und einem Buchstaben ab. Als Großbuchstabe geschrieben, bedeuten sie das Gegenteil.

SchreibweiseBedeutungEtwa dieselbe Klasse wie
\deine Ziffer[0-9]
\Dein Zeichen, das keine Ziffer ist[^0-9]
\wein Buchstabe, eine Ziffer oder ein Unterstrich (auch Hangul)weiter als [a-zA-Z0-9_]
\Wein Zeichen, das kein \w ist
\sein Leerraumzeichen (Leerzeichen, Tab, Zeilenumbruch …)weiter als [ \t\n\r\f\v]
\Sein 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

\D lässt auch Bindestriche und lateinische Buchstaben durch. Ersetz das \D in der Mitte durch [가-힣], damit nur echte Nummernschilder übrig bleiben.

2-5. In eckigen Klammern gelten andere Regeln (5 Min.)

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 Zeichen
re.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.

2-6. Unicode – und doppelte Symbole, die du meiden solltest (5 Min.)

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']
  • Willst du wirklich nur 0 bis 9, nimm [0-9] statt \d.
  • Das Flag 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 2

Heute 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.

Den Fall lösen (10 Min.)

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.

Übungen

  1. Schreib ein Muster, das im Garagenprotokoll nur die Namen der Ein- und Ausgänge wie B1 und B2 findet. Worin unterscheiden sich B[12] und B\d?
  2. Was findet [^\d\s]? Überleg es dir zuerst am Kameraprotokoll von oben, 12가3457 I2가3457 12가34S7 1Z거3407, und führe es dann aus.
  3. Schreib mit Kurzschreibweisen ein Muster für die Form zwei Ziffern, Doppelpunkt, zwei Ziffern, wie bei der Uhrzeit 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.

Erhoben: deine E-Mail-Adresse · Zweck: Hinweise auf neue Lektionen oder den Start dieses Kurses · Speicherdauer: bis du die Löschung verlangst. Keine andere Nutzung, keine Weitergabe. Antworte einfach auf eine Benachrichtigung, dann löschen wir sie sofort.

Fragen & Antworten

Frag alles zu dieser Lektion. Fragen aus allen Sprachversionen dieser Seite erscheinen hier gemeinsam.

Wird als reiner Text angezeigt, ohne Links oder Formatierung.

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
한국어English日本語简体中文EspañolDeutsch

© 2026 ReflexKR · Since 2026 · CC BY-NC 4.0

Built with Reflex