
Regex greedy vs lazy matching: waarom je patroon te veel pakt
Gepubliceerd op 24 jul 2026
Je schrijft <b>.*</b> om de inhoud van een bold-tag te pakken, test het op <b>Hello</b>, en het werkt perfect. Dan draai je het op echte HTML met twee bold-tags op dezelfde regel — <b>Hello</b> and <b>World</b> — en in plaats van twee matches krijg je één gigantische match over beide tags heen. Dit is geen bug in je regex-engine; het is het standaardgedrag van *, + en {n,m}, en de fix is één teken zodra je weet wat er gebeurt.
Waarom .* meer grijpt dan je verwacht
Standaard zijn quantifiers in regex greedy (gulzig) — .* betekent niet “match wat tekens”, het betekent “match zo veel mogelijk tekens, en geef alleen terrein prijs als de rest van het patroon dat absoluut vereist.”
Loop <b>.*</b> stap voor stap door tegen <b>Hello</b> and <b>World</b>:
<b>matcht de eerste<b>..*begint met het opslokken van de complete rest van de string — alles tot het einde, inclusief de tweede<b>World</b>.- De engine moet vervolgens
</b>vinden om de match af te maken, dus begint hij vanaf het einde van.*teken voor teken terug te geven. - De eerste plek (achterwaarts scannend vanaf het einde) waar
</b>past, is de allerlaatste</b>in de string — niet de eerste waar hij “logischerwijs” zou moeten stoppen.
De match wordt dus <b>Hello</b> and <b>World</b> in zijn geheel, omdat greedy matching altijd eerst de langst mogelijke string probeert en die alleen zo min mogelijk inkrimpt om de rest van het patroon te laten slagen.
De fix: maak de quantifier lazy met ?
Een ? direct achter een quantifier zet hem om van greedy naar lazy (ook wel “non-greedy” of “reluctant”): *?, +?, ??, {n,m}?.
Een lazy quantifier doet het omgekeerde: hij begint met zo weinig mogelijk tekens matchen, en breidt alleen uit als de rest van het patroon nog niet kan slagen.
<b>.*?</b> tegen dezelfde string:
<b>matcht de eerste<b>..*?begint met het matchen van nul tekens.- De engine checkt: matcht
</b>precies hier? Nee (we staan bij “Hello…”, nog niet bij een</b>) — dus breidt hij.*?met precies één teken uit en checkt opnieuw. - Dit herhaalt zich teken voor teken totdat
.*?preciesHelloheeft opgeslokt, waarna</b>direct matcht.
Resultaat: <b>Hello</b> en <b>World</b> komen terug als twee aparte matches, wat vrijwel altijd is wat je eigenlijk wilde bij het parsen van tag-achtige of delimiter-achtige structuren.
Wanneer je greedy juist wél wilt (het is niet zomaar “de verkeerde standaard”)
Greedy is geen fout in de taal — het is correct voor een ander, even veelvoorkomend geval: de buitenste grens van iets matchen, niet de kleinste eenheid erbinnen. Wil je “alles tussen de eerste { en de laatste }” uit een blob JSON-achtige tekst halen (bijvoorbeeld om een heel object te pakken ongeacht nesting), dan is greedy precies goed en zou lazy juist bij de eerste binnenste } stoppen, met een afgekapt, ongeldig fragment als resultaat.
De vuistregel: lazy voor herhaalde kleine afgebakende stukjes (tags, strings tussen aanhalingstekens, lijstitems); greedy voor “pak de hele buitenste reikwijdte.”
Snelle referentie
| Patroon | Gedrag | Gebruik het wanneer |
|---|---|---|
.*, .+, {n,m} |
Greedy — matcht zo lang mogelijk | Je de buitenste reikwijdte wilt, of er maar één match in de string zit |
.*?, .+?, {n,m}? |
Lazy — matcht zo kort mogelijk | Je meerdere vergelijkbare afgebakende stukjes hebt en elk apart wilt |
Weet je niet zeker wat een patroon werkelijk doet op je echte invoer in plaats van je teststring, draai het dan tegen de echte multi-match-tekst in een regex tester met live match-highlighting — het verschil wordt direct zichtbaar: één groot gemarkeerd blok voor greedy versus meerdere aparte blokken voor lazy. Dat is meestal sneller dan het teken voor teken beredeneren.