Catastrofaal backtracken, en waarom matchen in een worker draait
(a+)+$ kan een reeks van 30 "a" tekens op 229 manieren in groepen verdelen, en als de $ aan het eind faalt, backtrackt de engine er stuk voor stuk doorheen. Elke extra "a" verdubbelt het werk. Dit zit in backtrackend matchen zelf: JavaScript, re van Python en PCRE hebben het allemaal, en daarom laten RE2 en de regex-crate van Rust backreferences en lookaround vallen om het uit te sluiten.
Een JavaScript-engine kun je niet midden in een match onderbreken, dus de oplossing is isolatie: elke match draait in een Web Worker met een budget van één seconde, en een worker die daaroverheen gaat wordt afgebroken in plaats van dat hij het tabblad bevriest. Haal in je eigen patroon de dubbelzinnigheid weg: (a+)+ schrijf je als gewoon a+. JavaScript kent geen atomic groups of possessieve quantifiers, dus de binnenste groep herschrijven is de enige uitweg.
Genummerde en benoemde groepen
match.groups geeft benoemde captures op naam, maar niets in de RegExp API zegt welk nummer een naam krijgt. Dit telt openingshaakjes van links naar rechts, dezelfde regel die de engine gebruikt. Een niet-capturende groep (?:…) en een lookaround zoals (?=…) kosten geen nummer, en daarom eindigen twee groepen vaak op 1 en 3 in plaats van 1 en 2.
Veelvoorkomende problemen
- Een groep toont "geen match". Hij verloor een
|alternatie, of zat in een optionele groep die niet liep. Zijn waarde is ook in JavaScriptundefined, geen lege string. - Maar één match terwijl er meer zouden moeten zijn. Zonder
gkomt alleen de eerste terug, net als bijRegExp.exec. - "Evaluatie gestopt na 1 seconde". Zie catastrofaal backtracken hierboven.
Veelgestelde vragen
Wat is het verschil tussen "g" en "y"?
Global zoekt vooruit naar de volgende match waar dan ook. Sticky matcht alleen op de positie waar hij al staat, en dat is wat een met de hand geschreven tokenizer wil.
Kan ik hier een patroon testen dat voor Python of PCRE geschreven is?
Alleen bij benadering. JavaScript kent geen possessieve quantifiers of atomic groups, geen \A of \Z-ankers, en heeft eigen regels voor \w.
Waarom gooit een patroon dat eerst werkte een fout zodra ik "u" aanzet?
u maakt de engine streng over escapes. Een losse backslash voor een gewone letter wordt zonder die vlag stil genegeerd en is er met die vlag een syntaxisfout. Dat vangt typefouten, en het betekent dat een slordig patroon stopt met werken zodra u aanstaat.
Waarom liet mijn patroon de pagina vastlopen?
Dat deed het niet, en daar is de worker voor. Een patroon met geneste quantifiers kan exponentieel lang doen over een bijna-match, en een lopende match is niet te onderbreken. Door hem op een achtergrondthread te draaien met een budget van één seconde wordt een op hol geslagen patroon afgebroken in plaats van dat het tabblad bevriest.