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 5

Zeit 3:20–4:10

Daten in jedem Format: Gruppen und Alternativen

CASE-005

Grep & Co. Datendetektei

Fallakte

Daten in jedem Format

Auftraggeber

Frau Jeong, Betreiberin des Onlineshops „Markt Jeong“

Beweismittel

Die Bestellprotokolle von Markt Jeong. Jede Aushilfe hat das Datum in einem anderen Format eingetragen.

Zum Abschluss des Falls

Zieh aus jeder Bestellung Jahr, Monat und Tag einzeln heraus und bring alles in dasselbe Format.

Am Tag nach dem Fall mit den falschen Anmeldungen steht Frau Jeong wieder in der Tür. Diesmal seufzt sie zuerst. „Die Anmeldungen sind dicht. Aber jetzt will ich die Bestellungen nach Datum sortieren, und meine drei Aushilfen haben das Datum jede anders eingetragen.“ Die Chefin nimmt einen Schluck Kaffee. „Finden hast du in Teil 1 gelernt. Jetzt lernst du, aus dem Gefundenen etwas herauszuholen.“

So sehen die Bestellungen aus:

#1001 2026-10-20 ship=done
#1002 2026.10.20 ship=wait
#1003 2026/10/19 ship=done
#1004 20261019 ship=done
#1005 2026-10/20 ship=wait

Das Datum nur zu finden, reicht nicht. Um aufzuräumen, musst du „2026“, „10“ und „20“ einzeln herausholen. Das Werkzeug dafür sind heute runde Klammern, also Gruppen.

5-1. Mit Klammern zusammenfassen: Quantoren für Gruppen (5 Min.)

In Lektion 3 hast du gelernt, dass ein Quantor nur am Baustein direkt davor hängt. Mit runden Klammern ( ) werden mehrere Zeichen zu einem Baustein.

re.fullmatch(r"ha+", "hahaha")      # None
re.fullmatch(r"(ha)+", "hahaha")    # <re.Match object; span=(0, 6), match='hahaha'>

Auch Formen, in denen sich „Zahl und Punkt“ dreimal wiederholen, wie bei einer IP-Adresse, schreibst du mit einem Quantor an einer Gruppe.

re.search(r"(\d{1,3}\.){3}\d{1,3}", "ip=203.0.113.66")
# <re.Match object; span=(3, 15), match='203.0.113.66'>

(\d{1,3}\.){3} heißt dreimal „1 bis 3 Ziffern und ein Punkt“. Hinter der letzten Zahl steht kein Punkt, deshalb kommt \d{1,3} noch extra dazu.

5-2. Alternativen: | – dies oder das (8 Min.)

| heißt „links oder rechts“. Allerdings hat | im Regex den niedrigsten Vorrang von allen. Wenn du es nicht mit Klammern eingrenzt, teilt es das ganze Muster in zwei Hälften.

re.fullmatch(r"ship=done|wait", "wait")          # <re.Match object; span=(0, 4), match='wait'>
re.fullmatch(r"ship=(done|wait)", "wait")        # None
re.fullmatch(r"ship=(done|wait)", "ship=wait")   # <re.Match object; span=(0, 9), match='ship=wait'>

ship=done|wait heißt „ship=done oder wait“. Willst du „ship=, gefolgt von done oder wait“, setzt du nur die Auswahl in Klammern.

Besonders aufpassen musst du in Kombination mit Ankern. ^a|b$ heißt nicht „ein String, der nur aus a oder b besteht“, sondern „^a oder b$“.

re.findall(r"^a|b$", "a.b")         # ['a', 'b']
re.search(r"^ok|no$", "say no")     # <re.Match object; span=(4, 6), match='no'>

say no beginnt nicht mit ok und passt trotzdem. Gemeint war vermutlich ^(ok|no)$.

Die Alternativen werden von links nach rechts ausprobiert, und die erste, die klappt, gewinnt. Die längste wird nicht gesucht.

re.search(r"10|100", "100")   # <re.Match object; span=(0, 2), match='10'>
re.search(r"100|10", "100")   # <re.Match object; span=(0, 3), match='100'>
  • Stell längere Alternativen nach vorn.
  • Eine Auswahl zwischen einzelnen Zeichen wie -|\.|/ ist dasselbe wie die Klasse [-./]. In solchen Fällen ist die Klasse kürzer und schneller.

Beweislabor

re.search(r"", text)

Rückgabewert

ship=done|wait heißt 'ship=done' oder 'wait'. Setz nur die Auswahl in Klammern.

5-3. Herausholen über Gruppennummern (8 Min.)

Klammern haben neben dem Zusammenfassen noch eine zweite Aufgabe: Sie merken sich, was in ihnen gefunden wurde. Das nennt man eine Capture-Gruppe (fangende Gruppe), und sie werden in der Reihenfolge der öffnenden Klammern mit 1, 2, 3 … nummeriert.

Nimm an, die Bestellungen von oben stecken im String orders.

m = re.search(r"(\d{4})-(\d\d)-(\d\d)", orders)
print(m.group(0))               # 2026-10-20
print(m.group(1))               # 2026
print(m.group(2), m.group(3))   # 10 20
print(m.groups())               # ('2026', '10', '20')
print(m.group(1, 2))            # ('2026', '10')
print(m[1], m[0])               # 2026 2026-10-20
  • group(0) (oder einfach group()) ist der ganze Treffer. Ab Nummer 1 kommen die Gruppen.
  • groups() liefert alle Gruppen ab Nummer 1 als Tupel.
  • Gibst du wie bei group(1, 2) mehrere Nummern an, kommt ein Tupel zurück.
  • m[1] ist die Kurzform von m.group(1).

Auch bei verschachtelten Klammern gilt dieselbe Regel: Gezählt wird in der Reihenfolge der öffnenden Klammern (.

((\d{4})-(\d\d))-(\d\d)
12       3       4
m = re.search(r"((\d{4})-(\d\d))-(\d\d)", "2026-10-20")
print(m.groups())     # ('2026-10', '2026', '10', '20')

Beweislabor

re.search(r"", text)

Rückgabewert

Setz Jahr, Monat und Tag jeweils in Klammern. Jede Gruppe bekommt eine Farbe, und darunter erscheinen m.group(1) bis (3).

5-4. Klammern, die nichts fangen: (?:...) (5 Min.)

Oft willst du nur zusammenfassen, ohne dir etwas zu merken, etwa um die Reichweite von | festzulegen oder einen Quantor anzuhängen. Dafür gibt es (?:...). Es fasst zusammen, bekommt aber keine Nummer.

m = re.search(r"(?:\d{4})-(\d\d)", "2026-10")
print(m.group(1))                           # 10
print(re.fullmatch(r"(?:ab)+", "abab").groups())   # ()

Weil die erste Klammer keine Nummer bekommt, ist (\d\d) jetzt Gruppe 1. Fängst du nur, was du brauchst, bleiben die Nummern übersichtlich, und du umgehst die findall-Falle aus dem nächsten Abschnitt.

5-5. findall und Gruppen (8 Min.)

Hat das Muster Capture-Gruppen, verhält sich findall anders.

Anzahl Capture-Gruppenfindall liefert
0eine Liste der ganzen Treffer
1eine Liste der Strings dieser Gruppe
2 oder mehreine Liste von Tupeln der Gruppen
re.findall(r"\d{4}[-./]\d\d[-./]\d\d", orders)
# ['2026-10-20', '2026.10.20', '2026/10/19', '2026-10/20']
re.findall(r"(\d{4})[-./]\d\d[-./]\d\d", orders)
# ['2026', '2026', '2026', '2026']
re.findall(r"(\d{4})[-./](\d\d)[-./](\d\d)", orders)
# [('2026', '10', '20'), ('2026', '10', '20'), ('2026', '10', '19'), ('2026', '10', '20')]

Brauchst du Jahr, Monat und Tag, ist die Tupelliste praktisch. Das Problem: Auch Klammern, die du nur zum Zusammenfassen gesetzt hast, werden zu Gruppen.

re.findall(r"\d{4}(-|\.|/)\d\d(-|\.|/)\d\d", orders)
# [('-', '-'), ('.', '.'), ('/', '/'), ('-', '/')]

Gewollt war das Datum, zurück kamen nur die Trennzeichen. Brauchst du den ganzen Treffer, mach aus den Klammern (?:...) oder hol dir mit finditer die Match-Objekte und nimm group().

re.findall(r"\d{4}(?:-|\.|/)\d\d(?:-|\.|/)\d\d", orders)
# ['2026-10-20', '2026.10.20', '2026/10/19', '2026-10/20']
[m.group() for m in re.finditer(r"\d{4}(-|\.|/)\d\d(-|\.|/)\d\d", orders)]
# ['2026-10-20', '2026.10.20', '2026/10/19', '2026-10/20']

Dots Tipp

Wenn findall seltsam kurze Ergebnisse oder Tupel liefert, zähl zuerst die Klammern im Muster. Ganz oft hat sich eine Klammer, die nur zusammenfassen sollte, heimlich in eine Gruppe verwandelt. Darum kam in Lektion 3 bei (ha)+ mit findall auch nur 'ha' heraus.

Beweislabor

re.search(r"", text)

Rückgabewert

findall liefert Tupel mit Trennzeichen. Mach aus beiden Klammern (?: oder wähle finditer, damit das ganze Datum herauskommt.

5-6. Gruppen ohne Treffer, wiederholte Gruppen (6 Min.)

Nimmt eine Gruppe am Treffer nicht teil, steht an ihrer Stelle None. Das passiert, wenn eine Gruppe mit ? null-mal gepasst hat oder bei | die andere Seite gewählt wurde.

m = re.fullmatch(r"(\d{4})-(\d\d)(-(\d\d))?", "2026-10")
print(m.groups())     # ('2026', '10', None, None)
print(m.group(3))     # None
print(re.search(r"(a)|(b)", "b").groups())   # (None, 'b')

Hängt an einer Gruppe ein Quantor und passt sie mehrmals, bleibt in der Gruppe nur der letzte Durchgang übrig.

m = re.fullmatch(r"(\d)+", "2026")
print(m.group(0), m.group(1))   # 2026 6

re.findall(r"(\d{1,3}\.){3}\d{1,3}", "a=203.0.113.66 b=198.51.100.7")
# ['113.', '100.']
re.findall(r"(?:\d{1,3}\.){3}\d{1,3}", "a=203.0.113.66 b=198.51.100.7")
# ['203.0.113.66', '198.51.100.7']

(\d)+ hat viermal gepasst, aber in Gruppe 1 ist nur die letzte 6 geblieben. Auch bei der IP-Adresse blieb in der Gruppe nur der dritte Durchgang 113. übrig, und findall hat genau diese Gruppe zurückgegeben. Willst du das IP-Muster aus 5-1 mit findall benutzen, ist (?:...) die richtige Wahl.

Den Fall lösen (10 Min.)

Die drei Trennzeichen -, . und / deckst du mit [-./] ab, das trennzeichenlose 20261019 mit ?. Jahr, Monat und Tag fängst du jeweils einzeln.

Die Ausgabe ist koreanisch formatiert: 년 heißt Jahr, 월 Monat und 일 Tag.

DATE = r"(\d{4})[-./]?(\d\d)[-./]?(\d\d)"

for m in re.finditer(DATE, orders):
    y, mo, d = m.groups()
    print(f"{y}년 {int(mo)}월 {int(d)}일")
# 2026년 10월 20일
# 2026년 10월 20일
# 2026년 10월 19일
# 2026년 10월 19일
# 2026년 10월 20일

Willst du die Bestellnummer gleich mit herausholen, kommt eine weitere Gruppe dazu. Bei vier Gruppen liefert findall Tupel mit vier Feldern.

re.findall(r"#(\d+) " + DATE, orders)
# [('1001', '2026', '10', '20'), ('1002', '2026', '10', '20'), ('1003', '2026', '10', '19'), ('1004', '2026', '10', '19'), ('1005', '2026', '10', '20')]

Beweislabor

re.search(r"", text)

Rückgabewert

Dieses Datumsmuster akzeptiert nur Bindestriche. Ersetz die Trennzeichen durch [-./]?, damit alle vier Formate durchkommen.

Fall abgeschlossen — Die Daten aller 5 Bestellungen wurden in Jahr, Monat und Tag zerlegt und vereinheitlicht. Bei #1005 wurden allerdings gemischte Trennzeichen eingegeben, das muss das Personal noch prüfen.

Frau Jeong nimmt die aufgeräumte Tabelle entgegen und zeigt auf die letzte Zeile. „#1005 ist 2026-10/20. Das ist auch einfach durchgerutscht, oder?“

re.fullmatch(DATE, "2026-10/20")
# <re.Match object; span=(0, 10), match='2026-10/20'>

Stimmt. Die beiden [-./]? wissen nichts voneinander. Im jetzigen Muster gibt es keine Möglichkeit zu sagen: „Wenn vorn ein - stand, muss hinten auch ein - stehen.“ Die Chefin notiert eine Zeile in ihr Notizbuch. „Es gibt einen Weg, etwas Gefangenes weiter hinten noch einmal aufzurufen. Der kommt in Lektion 10.“ Für heute bleibt es dabei, dass ein Mensch diesen einen Eintrag mit eigenen Augen prüft.

Übungen

  1. Was ergibt re.findall(r"ship=(done|wait)", orders)? Was ändert sich, wenn du (?:done|wait) daraus machst?
  2. Was liefert groups() bei re.search(r"(\d{4})-(\d\d)(-(\d\d))?", "2026-10 Versand")?
  3. Schreib ein Muster, das aus der Uhrzeit 03:11:02 Stunde, Minute und Sekunde als Gruppen herausholt, und sieh dir das Ergebnis von m.group(1, 3) an.

Das Notizbuch der Direktorin

  • ( ) fasst zu einem Baustein zusammen und merkt sich den gefundenen Text. Quantoren anhängen wie bei (ab)+.

  • | hat den niedrigsten Vorrang. ^a|b$ heißt '^a oder b$'. Alternativen werden von links probiert.

  • Gruppennummern folgen den öffnenden Klammern. group(0) ist alles, groups() ein Tupel, m[1] ist group(1).

  • (?:...) fasst nur zusammen und bekommt keine Nummer.

  • findall: 0 Gruppen der ganze Treffer, 1 Gruppe diese Gruppe, ab 2 Gruppen Tupel. Für den ganzen Treffer (?:...) oder finditer.

  • Gruppen ohne Treffer sind None. Bei wiederholten Gruppen wie (\d)+ bleibt nur der letzte Durchgang.

← Zurück

Lektion 4 · Die falschen Anmeldungen: Anker und Validierung

Weiter →

Lektion 6 · Der Zugriff um 3 Uhr nachts: benannte Gruppen und Match-Objekte

Ü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