
Regex godus prieš tingų atitikimą: kodėl jūsų šablonas pagauna per daug
Paskelbta 2026-07-24
Parašote <b>.*</b>, kad pagautumėte pusjuodžio žymos turinį, išbandote su <b>Hello</b>, ir jis veikia puikiai. Tada paleidžiate jį su realiu HTML, kuriame toje pačioje eilutėje yra dvi pusjuodžio žymos — <b>Hello</b> and <b>World</b> — ir vietoj dviejų atitikmenų gaunate vieną milžinišką atitikmenį, apimantį abi žymas. Tai ne jūsų regex variklio klaida; tai numatytasis *, + ir {n,m} elgesys, ir tai turi vieno simbolio sprendimą, kai žinote, kas vyksta.
Kodėl .* pagauna daugiau, nei tikitės
Pagal nutylėjimą regex kiekikliai yra godūs — .* nereiškia „atitikti kelis simbolius“, tai reiškia „atitikti kiek įmanoma daugiau simbolių, tada atsitraukti tik jei likusi šablono dalis tai absoliučiai reikalauja“.
Peržiūrėkime <b>.*</b> prieš <b>Hello</b> and <b>World</b> žingsnis po žingsnio:
<b>atitinka pirmąjį<b>..*pradeda vartodamas visą likusią eilutės dalį — viską iki pabaigos, įskaitant antrąjį<b>World</b>.- Tada variklis turi surasti
</b>, kad užbaigtų atitikimą, todėl pradeda trauktis nuo.*pabaigos po vieną simbolį. - Pirmoji vieta (skenuojant atbulai nuo pabaigos), kurioje tinka
</b>, yra pati paskutinė</b>eilutėje — ne pirmoji, kurioje „logiškai“ turėtų sustoti.
Taigi atitikmuo galiausiai tampa visa <b>Hello</b> and <b>World</b>, nes godus atitikimas visada pirmiausia bando ilgiausią įmanomą eilutę ir sumažina ją tik tiek, kiek būtina, kad likusi šablono dalis pasisektų.
Sprendimas: padaryti kiekiklį tingų su ?
Pridėjus ? iškart po kiekiklio, jis pasikeičia iš godaus į tingų (dar vadinamą „negodų“ arba „nenoriai atitinkantį“): *?, +?, ??, {n,m}?.
Tingus kiekiklis daro priešingai: pradeda atitikdamas kiek įmanoma mažiau simbolių, tada plečiasi tik jei likusi šablono dalis dar negali pasisekti.
<b>.*?</b> prieš tą pačią eilutę:
<b>atitinka pirmąjį<b>..*?pradeda atitikdamas nulį simbolių.- Variklis patikrina: ar
</b>atitinka čia pat? Ne (esame prie „Hello…“, dar ne prie</b>) — todėl.*?plečiamas tiksliai vienu simboliu ir tikrinama iš naujo. - Tai kartojasi simbolis po simbolio, kol
.*?suvartoja tiksliaiHello, kuriuo momentu</b>atitinka iškart.
Rezultatas: <b>Hello</b> ir <b>World</b> grąžinami kaip du atskiri atitikmenys, o tai beveik visada yra tai, ko iš tikrųjų norėjote, analizuodami žymoms ar skirtukams panašias struktūras.
Kada iš tikrųjų norite godaus (tai ne tik „neteisingas numatytasis“)
Godumas nėra kalbos klaida — jis teisingas kitu, vienodai dažnu atveju: atitikti kažko išorinę ribą, ne mažiausią vidinį vienetą. Jei išgaunate „viską tarp pirmojo { ir paskutiniojo }“ JSON panašiame teksto gabale (tarkim, kad pagautumėte visą objektą, nepaisant įdėjimo), godumas yra tiksliai teisingas, o tingumas sustotų prie pirmojo vidinio }, duodamas nukirstą, netinkamą fragmentą.
Nykščio taisyklė: tingus pasikartojantiems mažiems atskirtiems fragmentams (žymos, kabutėse esančios eilutės, sąrašo elementai); godus „pagauti visą išorinį intervalą“.
Greita nuoroda
| Šablonas | Elgesys | Naudokite, kai |
|---|---|---|
.*, .+, {n,m} |
Godus — atitinka ilgiausią įmanomą | Norite išorinio intervalo arba eilutėje yra tik vienas atitikmuo |
.*?, .+?, {n,m}? |
Tingus — atitinka trumpiausią įmanomą | Turite kelis panašius atskirtus fragmentus ir norite kiekvieno atskirai |
Jei nesate tikri, ką šablonas iš tikrųjų daro su realiais duomenimis, o ne su testine eilute, paleidus jį prieš tikrą kelių atitikmenų tekstą regex testeryje su gyvu atitikmenų paryškinimu, skirtumas iškart tampa akivaizdus — pamatysite vieną milžinišką paryškintą bloką godžiam prieš kelis atskirus tingiam, o tai dažniausiai greičiau nei samprotauti simbolis po simbolio.