CodeKitHub
Regex greedy vs lazy matching: waarom je patroon te veel pakt

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>:

  1. <b> matcht de eerste <b>.
  2. .* begint met het opslokken van de complete rest van de string — alles tot het einde, inclusief de tweede <b>World</b>.
  3. 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.
  4. 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:

  1. <b> matcht de eerste <b>.
  2. .*? begint met het matchen van nul tekens.
  3. 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.
  4. Dit herhaalt zich teken voor teken totdat .*? precies Hello heeft 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.

← Terug naar blog