CodeKitHub
Regex godus prieš tingų atitikimą: kodėl jūsų šablonas pagauna per daug

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:

  1. <b> atitinka pirmąjį <b>.
  2. .* pradeda vartodamas visą likusią eilutės dalį — viską iki pabaigos, įskaitant antrąjį <b>World</b>.
  3. Tada variklis turi surasti </b>, kad užbaigtų atitikimą, todėl pradeda trauktis nuo .* pabaigos po vieną simbolį.
  4. 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ę:

  1. <b> atitinka pirmąjį <b>.
  2. .*? pradeda atitikdamas nulį simbolių.
  3. 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.
  4. Tai kartojasi simbolis po simbolio, kol .*? suvartoja tiksliai Hello, 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.

← Grįžti į tinklaraštį