- 1 Section
- 10 Lessons
- unbegrenzt
- Reguläre Ausdrücke & Textverarbeitung10
- 1.1Was sind Reguläre Ausdrücke?
- 1.2Grundzeichen: Literale, Zeichenklassen, Quantoren
- 1.3Anker, Gruppen und Alternation
- 1.4Gierige vs. faule Quantoren
- 1.5Regex in Python: re-Modul
- 1.6Regex in JavaScript
- 1.7grep, sed, awk – Regex auf der Kommandozeile
- 1.8Regex für Validierung: E-Mail, IP, PLZ, Datum
- 1.9Regex in Datenbanken: LIKE, REGEXP
- 1.10Aufgaben Reguläre Ausdrücke
Gierige vs. faule Quantoren
Du kennst die Quantoren aus L2: *, +, ?, {n,m}. Was du noch nicht weißt: jeder dieser Quantoren hat zwei Modi. Standardmäßig sind sie gierig (greedy) – sie versuchen so viel zu matchen wie möglich. Mit einem nachgestellten ? werden sie faul (lazy) – sie matchen so wenig wie möglich.
Klingt nach Detail, ist aber einer der häufigsten Fehler-Quellen beim Regex-Schreiben. Wer den Unterschied nicht versteht, schreibt Patterns die zu viel matchen und sich wundert warum die Ausgabe Müll ist. Diese Lektion zeigt dir mit Animationen wie die Regex-Engine tatsächlich arbeitet – und wann du welchen Modus brauchst.
1) Das Beispiel das alle verwirrt
Stell dir vor du willst aus einem HTML-Fragment das erste Tag extrahieren. Du schreibst: „Suche <, dann irgendwas, dann >". Das übersetzt zu <.+>. Test:
| Eingabe | Pattern | Erwartet | Tatsächlich |
|---|---|---|---|
<b>Hallo</b> | <.+> | <b> | <b>Hallo</b> ⚠ |
Was?! Statt nur <b> hat es das komplette HTML inklusive aller Tags matched. Wo liegt das Problem? Im gierigen Verhalten von +: der Quantor versucht so viel wie möglich zu schlucken. Er sieht das erste <, dann „beliebig viele Zeichen", dann muss er ein > finden. Greedy heißt: er nimmt erst alle restlichen Zeichen, dann gibt er Zeichen für Zeichen zurück bis er ein > findet. Findet er das letzte > der Zeile – nicht das erste.
2) Wie die Regex-Engine wirklich denkt
Das Konzept dahinter heißt Backtracking. Schau dir die Schritte an. Die Engine arbeitet Zeichen für Zeichen, probiert maximal viel, kommt zurück wenn nötig:
3) Greedy vs. Lazy: die Tabelle
Jeder Quantor hat eine gierige (Standard) und eine faule Variante. Hier die Übersicht:
?. Wichtig: das ? hier ist eine Modifikator-Markierung, kein Quantor! +? ist nicht „+ oder nichts" – es ist „1+, aber bitte minimal". Der Modifikator ? nach einem Quantor schaltet auf lazy um.4) Side-by-Side-Vergleich
Schau die Auswirkungen am gleichen Text:
Match: <b>Hallo</b>
Match: <b>Hallo</b>
Match: HalloWelt
Match: HalloWelt
5) Wann brauche ich was?
Eine Faustregel für die Praxis:
- Greedy ist Standard – passt meistens für „nimm das ganze Wort", „nimm die ganze Zahl", „nimm bis Zeilenende".
- Lazy wenn du zwischen zwei Markierungen suchst die wiederholt vorkommen können. Klassiker: HTML-Tags, Quotes in einem String, Klammer-Inhalte.
Konkrete Beispiele:
| Aufgabe | Pattern | Modus |
|---|---|---|
| Alle Ziffern einer Zahl | \d+ | greedy ✓ |
| Ganze Zeile lesen | .+$ | greedy ✓ |
| Inhalt zwischen <b> und </b> | <b>.+?</b> | lazy ✓ |
| Inhalt zwischen Anführungszeichen | "[^"]*" oder ".*?" | lazy / Negation |
| Inhalt von HTML-Comment | <!--.*?--> | lazy ✓ |
| Erste E-Mail einer Zeile | \S+@\S+ | greedy ✓ |
Eine Alternative zu Lazy ist oft die negative Zeichenklasse: statt „beliebig viele Zeichen (faul) bis '" kannst du sagen „beliebig viele Nicht-'-Zeichen". Pattern "[^"]*" ist oft sauberer und schneller als ".*?". Weil keine Backtracking-Schleifen nötig sind.
6) Possessive Quantoren – die dritte Variante
Manche Regex-Engines (Java, PCRE, neuere Python-Versionen) kennen einen dritten Modus: possessive Quantoren mit nachgestelltem +. Sie sind wie greedy – aber geben nichts mehr zurück, kein Backtracking:
Possessive sind selten nötig, dafür aber extrem schnell – kein Backtracking bedeutet vorhersagbare Performance. Sie verhindern auch katastrophales Backtracking (siehe nächster Abschnitt). In JavaScript gibt's das nicht – dort musst du mit „atomic groups" oder Negation arbeiten.
7) Katastrophales Backtracking
Jetzt zum dunklen Geheimnis der Regex: schlecht geschriebene Patterns mit Quantoren können exponentielle Laufzeit verursachen. Das nennt man catastrophic backtracking. Klassiker:
(a+)+b auf „aaaaaaaaaaaaaaaaaaaaaaaaaaX" (kein b am Ende). Die Engine probiert exponentiell viele Möglichkeiten wie die a's auf die beiden geschachtelten Quantoren verteilt werden können. Bei 25 a's: einige Sekunden Hänger. Bei 30 a's: Minuten. Bei 35 a's: Stunden. Das ist auch der Angriffsvektor für ReDoS (Regular Expression Denial of Service) – Angreifer schicken solche Eingaben um Server zum Hängen zu bringen.Wie vermeiden?
- Keine geschachtelten Quantoren auf überlappenden Mengen:
(a+)+,(a*)*,(a|aa)+ - Spezifische Patterns statt „alles":
"[^"]*"statt".*?", weil[^"]nicht mit dem Schluss-Quote konkurrieren kann - Possessive Quantoren oder Atomic Groups wenn die Sprache sie unterstützt
- Zeitlimits beim Regex-Einsatz auf User-Input (in Secure Coding diskutiert)
Tools wie regex101.com erkennen problematische Patterns und warnen. In API-Code der User-Input gegen Regex prüft, ist das ein realistisches Sicherheitsrisiko.
8) Trick: negative Klassen als Lazy-Ersatz
Eine elegante Technik die viele Probleme löst: statt „greedy mit Backtracking" oder „lazy" nimmst du eine negierte Zeichenklasse. Beispiele:
- String in Quotes:
"[^"]*"statt".*?" - HTML-Tag:
<[^>]+>statt<.+?> - URL bis nächstem Space:
https?://[^\s]+statthttps?://.+?(?=\s|$)
Der Vorteil: kein Backtracking. Die Engine geht linear durch und stoppt sobald das verbotene Zeichen auftaucht. Schneller, weniger Risiko für katastrophales Backtracking, oft klarer lesbar. Faustregel: wenn du .*? oder .+? schreibst und es ein klares „Stopp-Zeichen" gibt – nimm die negierte Klasse.
9) Greedy mit Anker = vorhersagbar
Wenn du $-Anker einsetzt, ändert sich das Greedy-Verhalten nicht – aber es wird vorhersagbarer. Pattern .+$ matched die ganze Zeile, weil das Greedy alles schluckt und der Anker am Ende sowieso erfüllt ist. Pattern ^.+$ matched die ganze Zeile zwischen Anfang und Ende. Bei Zeilen-orientierten Eingaben sehr nützlich.
Klassiker: Log-Zeilen parsen. ^\[(\w+)\] (.+)$ – Anfang der Zeile, eckige Klammer mit Level (Gruppe 1), Space, Rest der Zeile bis Ende (Gruppe 2). Greedy auf Gruppe 2 ist hier richtig: wir wollen die ganze Restzeile.
10) Debugging-Tipps
Wenn ein Pattern nicht das tut was du willst und du Greedy/Lazy verdächtigst:
- Pattern in den Tester (regex101.com, regexr.com, das Lab aus L1). Sieh sofort was matched.
- Längst-Match prüfen: ist das Match-Ende viel weiter rechts als erwartet? → Greedy-Problem.
- Test mit Lazy-Variante: ändere
+zu+?und schau ob's besser passt. - Alternative: negierte Klasse: wenn Lazy ein Stopp-Zeichen hat, lieber
[^Z]*wo Z das Stopp-Zeichen ist. - Step Through: regex101 hat einen Step-Debugger der jeden Backtrack zeigt. Sehr lehrreich!
Zusammenfassung
Jeder Quantor hat zwei Modi: greedy (Standard, maximal schlucken) und lazy (mit nachgestelltem ?, minimal). Klassisches Problem: <.+> matched ganzes HTML, <.+?> nur erstes Tag. Greedy für „bis Zeilenende" / ganze Wörter, lazy für „zwischen zwei Markierungen". Katastrophales Backtracking: verschachtelte Quantoren wie (a+)+ können exponentielle Laufzeit haben (ReDoS-Risiko!). Eleganter Lazy-Ersatz: negierte Zeichenklasse [^X]* statt .*? – kein Backtracking, sauberer, schneller.
