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 3

Zeit 1:40–2:30

Das zerrissene Telefonbuch: Quantoren

CASE-003

Grep & Co. Datendetektei

Fallakte

Das zerrissene Telefonbuch

Auftraggeber

Die Floristin vom Blumenladen „Wildblume“ in der Gasse

Beweismittel

Die per OCR eingelesene Liste aus dem verregneten, zerrissenen Telefonbuch der Stammkundschaft. Jede Nummer hat andere Trennzeichen, und zwischendrin stehen Zahlen, die da nicht hingehören.

Zum Abschluss des Falls

Sammle alle Telefonnummern und Postleitzahlen aus der Liste.

Als du morgens ankommst, hat die Chefin das Nummernschild-Muster von gestern auf dem Bildschirm. „Statt viermal \d zu schreiben, kann man auch einfach ‚viermal‘ hinschreiben. Das lernst du heute.“ In dem Moment geht die Tür auf, und die Floristin aus dem Blumenladen in der Gasse kommt mit einem Packen nasser Blätter herein. „Das Telefonbuch meiner Stammkunden ist im Regen völlig aufgeweicht. Ich hab es eingescannt und in Text umwandeln lassen, aber das ist ein einziges Chaos.“

So sieht die per OCR gelesene Liste aus. Jede Nummer hat ein anderes Trennzeichen, und mit der letzten Zeile stimmt irgendwas nicht.

kim   010-0000-1234   04542
lee   010 0000 9876   04545
park  02-0000-5678    04538
choi  010.0000.4321   04539
yoon  1234-5678-9012

Die Syntax, mit der du festlegst, wie oft etwas vorkommt, heißt Quantor. Heute bringst du mit Quantoren diese Liste in Ordnung.

3-1. Genau so oft: {m} und {m,n} (7 Min.)

Ein Quantor legt fest, wie oft der eine Baustein direkt davor wiederholt wird. \d{4} ist dasselbe wie \d\d\d\d. Kürzen wir damit das Nummernschild-Muster aus Lektion 2.

re.findall(r"\d{2}[가-힣]\d{4}", "12가3457 123가4567")   # ['12가3457', '23가4567']

Kürzer ist es, aber da ist ein Problem. Die neuen Nummernschilder, die es seit 2019 gibt, haben vorn drei Ziffern. Bei 123가4567 wurde die vordere 1 weggeworfen, und nur 23가4567 kam heraus.

{m,n} heißt mindestens m- und höchstens n-mal. Ändere den vorderen Teil in \d{2,3}, dann werden zwei- und dreistellige Anfänge akzeptiert.

re.findall(r"\d{2,3}[가-힣]\d{4}", "12가3457 123가4567")   # ['12가3457', '123가4567']
  • Schreib bei {2,3} keine Leerzeichen vor oder nach das Komma. Mit Leerzeichen ist es kein Quantor mehr (siehe Dots Tipp unten).
  • Eine Postleitzahl hat hier fünf Ziffern, also \d{5}.

Beweislabor

re.search(r"", text)

Rückgabewert

Das lange Muster aus Lektion 2. Kürz es mit \d{2,3} und \d{4}, sodass auch die neuen dreistelligen Nummernschilder durchkommen.

3-2. Mindestens, höchstens: {m,} und {,n} (6 Min.)

Bei {m,n} darfst du eine Seite leer lassen.

  • {m,} : mindestens m-mal, nach oben offen
  • {,n} : null- bis n-mal. Weil auch null erlaubt ist, passt es auch auf eine Stelle ganz ohne Zeichen
re.findall(r"a{2,}", "a aa aaaa")   # ['aa', 'aaaa']
re.findall(r"\d{3,}", "1 22 333 4444")   # ['333', '4444']
re.findall(r"\d{,2}", "123")        # ['12', '3', '']

Das '' am Ende des letzten Ergebnisses ist ein Treffer ganz am Ende des Strings, an einer Stelle mit null Ziffern. Solche leeren Treffer schauen wir uns in 3-6 genauer an.

Sieht eine { nicht wie ein Quantor aus, wird sie als normales Zeichen behandelt. a{x} sucht die vier Zeichen a, {, x und }.

re.findall(r"a{x}", "a{x} ax")      # ['a{x}']
re.findall(r"a{2, 3}", "aa a{2, 3}")   # ['a{2, 3}']

Dots Tipp

Ich hab mal a{2, 3} geschrieben, mit einem Leerzeichen nach dem Komma. Kein Fehler, nichts, es hat einfach still und leise was völlig anderes gesucht. Was nicht wie ein Quantor aussieht, gilt eben als normaler Text. Kein Fehler heißt nicht, dass das Muster stimmt. In geschweifte Klammern gehören nur Ziffern und ein Komma.

3-3. Die drei Häufigsten: * + ? (8 Min.)

Die gebräuchlichsten Anzahlen haben einen Namen aus nur einem Zeichen.

QuantorBedeutungGleichwertig
*null- oder mehrmals{0,}
+ein- oder mehrmals{1,}
?null- oder einmal (darf da sein, muss aber nicht){0,1}
re.findall(r"ab*", "a ab abbb")      # ['a', 'ab', 'abbb']
re.findall(r"ab+", "a ab abbb")      # ['ab', 'abbb']
re.findall(r"ab?", "a ab abbb")      # ['a', 'ab', 'ab']
re.findall(r"colou?r", "color colour colouur")   # ['color', 'colour']

? passt perfekt für „ein Zeichen, das da sein darf, aber nicht muss“. Bei den Handynummern in der Liste ist das Trennzeichen mal ein Bindestrich, mal ein Leerzeichen, mal ein Punkt. Schreibst du für das Trennzeichen [-. ]?, darf eins der drei dastehen oder auch gar keins.

re.findall(r"010[-. ]?\d{4}[-. ]?\d{4}", phonebook)
# ['010-0000-1234', '010 0000 9876', '010.0000.4321']

Wie in Lektion 2 gelernt, ist . in eckigen Klammern ein echter Punkt, und - steht ganz vorn und ist deshalb ein normales Zeichen.

Beweislabor

re.search(r"", text)

Rückgabewert

Dieses Muster akzeptiert nur Bindestriche. Ersetz die Trennzeichen durch [-. ]?, damit auch Leerzeichen, Punkte und Nummern ganz ohne Trennzeichen durchkommen.

3-4. Ein Quantor gilt nur für den Baustein direkt davor (5 Min.)

Ein Quantor hängt sich nur an den einen Baustein direkt vor ihm. Ein Baustein ist ein einzelnes Zeichen, eine Klasse in eckigen Klammern oder eine Kurzschreibweise wie \d. Deshalb heißt ab+ nicht „mehrmals ab“, sondern „ein a, gefolgt von mehreren b“.

re.findall(r"ab+", "abbb abab")      # ['abbb', 'ab', 'ab']
re.fullmatch(r"ab+", "abab")         # None
re.fullmatch(r"(ab)+", "abab")       # <re.Match object; span=(0, 4), match='abab'>

Willst du mehrere Zeichen zusammen wiederholen, fasst du sie mit runden Klammern zu einem Baustein zusammen. (ab)+ heißt „ein- oder mehrmals ab“. Klammern haben außer dem Zusammenfassen noch eine zweite wichtige Aufgabe. Die lernst du in Lektion 5.

re.fullmatch liefert nur dann einen Treffer, wenn der ganze String zum Muster passt. Den genauen Unterschied behandeln wir in Lektion 4.

Beweislabor

re.search(r"", text)

Rückgabewert

ha+ wiederholt nur das a. Fasse ha mit Klammern zusammen und wiederhole das Ganze. (Mit findall sieht das Ergebnis seltsam aus. Warum, erfährst du in Lektion 5.)

3-5. Quantoren sind gierig (4 Min.)

Quantoren sind von Haus aus gierig (greedy). Sie nehmen sich so viel, wie es der erlaubte Bereich nur zulässt.

re.search(r"\d+", "tel 0100000")     # <re.Match object; span=(4, 11), match='0100000'>
re.search(r"\d{2,4}", "123456")      # <re.Match object; span=(0, 4), match='1234'>
re.findall(r"\d{2,4}", "123456")     # ['1234', '56']

\d{2,4} wäre auch mit zwei Ziffern zufrieden, schnappt sich aber zuerst vier. Erst wenn der Rest des Musters scheitert, gibt es eine nach der anderen wieder her. Dieses „Zurückgeben und neu Versuchen“ heißt Backtracking, und in Lektion 8 legt genau das die Detektei lahm. Den genügsamen Quantor +?, der sich möglichst wenig nimmt, lernst du dann auch.

3-6. Alles finden: findall und finditer (10 Min.)

Jetzt sammelst du die Treffer aus der ganzen Liste ein. Nimm an, die Liste von oben steckt im String phonebook.

re.findall gibt die gefundenen Texte als Liste von Strings zurück. So verhält es sich, wenn das Muster keine runden Klammern enthält. Was mit Klammern passiert, siehst du in Lektion 5.

re.findall(r"\d{5}", phonebook)     # ['04542', '04545', '04538', '04539']

re.finditer gibt einen Iterator zurück, der dir ein Match-Objekt nach dem anderen liefert. Den nimmst du, wenn du auch die Position (span) brauchst.

for m in re.finditer(r"\d{5}", phonebook):
    print(m.group(), m.span())
# 04542 (22, 27)
# 04545 (50, 55)
# 04538 (78, 83)
# 04539 (106, 111)
  • Ein Iterator sucht erst dann weiter, wenn du den nächsten Treffer haben willst. Das spart Speicher, auch bei sehr langen Strings.
  • Ist ein Iterator einmal durchgelaufen, ist er leer. Für einen zweiten Durchgang rufst du re.finditer noch einmal auf.

Vorsicht ist hier bei leeren Treffern geboten. *, ? und {,n} erlauben null Wiederholungen und haben deshalb auch an Stellen ohne ein einziges Zeichen Erfolg. findall packt auch diese leeren Treffer als '' ins Ergebnis.

re.findall(r"\d*", "a12b")    # ['', '12', '', '']
re.findall(r"\d+", "a12b")    # ['12']

Ein leerer Treffer vor dem a, dann 12, ein leerer Treffer vor dem b und einer am Ende des Strings. Wenn du etwas einsammeln willst, nimm + oder {1,}, die null Wiederholungen nicht zulassen.

Beweislabor

re.search(r"", text)

Rückgabewert

\d* hinterlässt an jeder Stelle ohne Ziffer einen leeren String. Mach aus dem * ein + und sammle nur die Ziffernblöcke ein.

Den Fall lösen (10 Min.)

Eine Telefonnummer besteht hier aus einer Vorwahl bzw. einem Anfang mit 2 bis 3 Ziffern, einem Mittelteil mit 3 bis 4 Ziffern und einem Schluss mit 4 Ziffern. Trennzeichen dürfen da sein oder fehlen. Eine Postleitzahl hat fünf Ziffern.

phones = re.findall(r"\d{2,3}[-. ]?\d{3,4}[-. ]?\d{4}", phonebook)
print(phones)
# ['010-0000-1234', '010 0000 9876', '02-0000-5678', '010.0000.4321', '234-5678-9012']

postcodes = re.findall(r"\d{5}", phonebook)
print(postcodes)   # ['04542', '04545', '04538', '04539']

Vier Telefonnummern und vier Postleitzahlen sind beisammen. Aber was ist die fünfte Telefonnummer, 234-5678-9012? Die Floristin wirft einen Blick auf den Bildschirm und schüttelt den Kopf. „Das ist keine Telefonnummer. Das ist eine Notiz mit meiner Kartennummer.“

re.search(r"\d{2,3}[-. ]?\d{3,4}[-. ]?\d{4}", "yoon  1234-5678-9012")
# <re.Match object; span=(7, 20), match='234-5678-9012'>

Das Muster hat die erste Ziffer 1 von 1234 übersprungen und den Treffer an Position 7 begonnen, mitten in einer Zahl. Dasselbe Missgeschick wie bei 23가4567 in 3-1.

Beweislabor

re.search(r"", text)

Rückgabewert

Das ist das Muster aus der Lösung. Sieh dir an, wie die Markierung in der letzten Zeile erst nach der 1 beginnt. Dieses Problem behebst du in Lektion 4.

Fall abgeschlossen — Aus der OCR-Liste wurden 4 Telefonnummern und 4 Postleitzahlen wiederhergestellt und an den Blumenladen übergeben. Offen bleibt, dass ein Teil einer Kartennummer fälschlich als Telefonnummer erkannt wird.

Die Chefin starrt lange auf die letzte Zeile. „Mitten in der Zahl angefangen. Dieses Muster weiß nicht, wo eine Nummer anfängt und wo sie aufhört.“ In der nächsten Lektion lernst du, diese Grenzen ins Muster zu schreiben.

Übungen

  1. Überleg dir, was \d{2,3}[-. ]?\d{3,4}[-. ]?\d{4} in 01000001234 (einer Nummer ohne Trennzeichen) findet, und probier es aus.
  2. Was ergibt re.findall(r"\d?", "a1")? Wie viele leere Strings sind dabei?
  3. Finde mit re.finditer die Telefonnummern in der Liste und gib Zeile für Zeile die Nummer und ihre Startposition aus.

Das Notizbuch der Direktorin

  • {m} genau m-mal, {m,n} m- bis n-mal, {m,} mindestens m-mal, {,n} null- bis n-mal. Keine Leerzeichen am Komma.

  • * null- oder mehrmals, + ein- oder mehrmals, ? null- oder einmal.

  • Ein Quantor gilt nur für den Baustein direkt davor. ab+ und (ab)+ sind verschieden.

  • Quantoren sind gierig. Sie nehmen sich so viel wie möglich.

  • re.findall liefert eine Liste von Strings, re.finditer einen Iterator über Match-Objekte.

  • Muster, die null Wiederholungen erlauben, erzeugen leere Treffer ''. Zum Einsammeln + nehmen.

← Zurück

Lektion 2 · Der Fluchtwagen: Zeichenklassen

Weiter →

Lektion 4 · Die falschen Anmeldungen: Anker und Validierung

Ü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