CodeKitHub
Regex ahne vs laisk sobitamine: miks su muster haarab liiga palju

Regex ahne vs laisk sobitamine: miks su muster haarab liiga palju

Avaldatud 24. juuli 2026

Kirjutad <b>.*</b>, et haarata paksu kirja sildi sisu, testid seda <b>Hello</b> peal ja see töötab täiuslikult. Siis käivitad selle päris HTML-il, kus samal real on kaks paksu kirja silti — <b>Hello</b> and <b>World</b> — ja kahe vaste asemel saad ühe hiigelvaste, mis hõlmab mõlemat silti. See pole viga sinu regex-mootoris; see on *, + ja {n,m} vaikekäitumine, ning sellel on ühe märgi pikkune lahendus, kui tead, mis toimub.

Miks .* haarab rohkem, kui ootasid

Vaikimisi on regexi kvantifikaatorid ahned.* ei tähenda “sobita mõned märgid”, see tähendab “sobita nii palju märke kui võimalik ja taandu ainult siis, kui ülejäänud muster seda absoluutselt nõuab.”

Käi läbi <b>.*</b> vastu <b>Hello</b> and <b>World</b> samm-sammult:

  1. <b> sobib esimese <b>-ga.
  2. .* alustab kogu ülejäänud stringi neelamisega — kõike kuni lõpuni, kaasa arvatud teine <b>World</b>.
  3. Mootor peab siis leidma </b>, et vaste lõpetada, nii et ta hakkab .* lõpust taganema ühe märgi kaupa.
  4. Esimene koht (lõpust tagasi skaneerides), kus </b> sobib, on stringi kõige viimane </b> — mitte esimene, kus ta “loogiliselt” peaks peatuma.

Nii et vaste lõpeb olemas terve <b>Hello</b> and <b>World</b>, sest ahne sobitamine proovib alati kõigepealt pikimat võimalikku stringi ja vähendab seda ainult nii vähe kui vajalik, et ülejäänud muster õnnestuks.

Lahendus: muuda kvantifikaator laisaks ?-ga

? lisamine kohe pärast kvantifikaatorit muudab selle ahnest laisaks (nimetatakse ka “mitte-ahneks” või “vastumeelseks”): *?, +?, ??, {n,m}?.

Laisk kvantifikaator teeb vastupidist: ta alustab nii väheste märkide sobitamisega kui võimalik, ning laieneb ainult siis, kui ülejäänud muster veel ei õnnestu.

<b>.*?</b> sama stringi vastu:

  1. <b> sobib esimese <b>-ga.
  2. .*? alustab nulli märgi sobitamisega.
  3. Mootor kontrollib: kas </b> sobib täpselt siin? Ei (oleme kohas “Hello…”, mitte veel </b>-s) — nii et ta laiendab .*? täpselt ühe märgi võrra ja kontrollib uuesti.
  4. See kordub märk-märgi haaval, kuni .*? on neelanud täpselt Hello, mille juures </b> sobib kohe.

Tulemus: <b>Hello</b> ja <b>World</b> tulevad tagasi kahe eraldi vastena, mis on peaaegu alati see, mida tegelikult sildi- või eraldajataoliste struktuuride parsimisel tahtsid.

Millal tahad tegelikult ahnust (see pole lihtsalt “vale vaikeseade”)

Ahnus pole keeles viga — see on õige teistsuguse, sama levinud juhtumi jaoks: millegi kõige välimise piiri sobitamiseks, mitte selle sees oleva väikseima üksuse. Kui ekstraktid “kõike esimese { ja viimase } vahel” JSON-taolises tekstiplokis (näiteks kogu objekti haaramiseks olenemata pesastusest), on ahnus täpselt õige ning laiskus peatuks selle asemel esimese sisemise } juures, andes sulle katkestatud, kehtetu fragmendi.

Rusikareegel: laiskus korduvate väikeste eraldatud tükkide jaoks (sildid, jutumärkides stringid, nimekirja elemendid); ahnus “haara kogu välimine ulatus” jaoks.

Kiirviide

Muster Käitumine Kasuta seda, kui
.*, .+, {n,m} Ahne — sobitab pikima võimaliku Tahad kõige välimist ulatust või stringis on vaid üks vaste
.*?, .+?, {n,m}? Laisk — sobitab lühima võimaliku Sul on mitu sarnast eraldatud tükki ja tahad igaüht eraldi

Kui pole kindel, kumba muster su päris sisendil (mitte testistringil) tegelikult teeb, siis selle käivitamine reaalse mitme-vastega teksti peal regexi testijas koos elava vaste esiletõstuga muudab erinevuse koheselt nähtavaks — näed ühte hiigelsuurt esiletõstetud plokki ahnuse puhul versus mitut eraldi plokki laiskuse puhul, mis on tavaliselt kiirem kui märk-märgi haaval läbi mõtlemine.

← Tagasi blogisse