CodeKitHub
Regex: greedy vs lazy matching — prečo váš vzor zoberie priveľa

Regex: greedy vs lazy matching — prečo váš vzor zoberie priveľa

Publikované 24. 7. 2026

Napíšete <b>.*</b>, aby ste vytiahli obsah tučnej značky, otestujete na <b>Hello</b>, funguje to dokonale. Potom to spustíte na reálnom HTML s dvoma tučnými značkami na rovnakom riadku — <b>Hello</b> and <b>World</b> — a namiesto dvoch zhôd dostanete jednu obriu zhodu cez obe značky. Nie je to chyba vášho regex enginu; je to predvolené správanie *, + a {n,m} a má opravu na jeden znak, keď viete, čo sa deje.

Prečo .* zoberie viac, než čakáte

Kvantifikátory v regexe sú v predvolenom stave greedy (chamtivé) — .* neznamená „zhoduj sa s nejakými znakmi“, ale „zhoduj sa s čo najviac znakmi a ustúp iba vtedy, ak to zvyšok vzoru bezpodmienečne vyžaduje“.

Prejdime <b>.*</b> voči <b>Hello</b> and <b>World</b> krok za krokom:

  1. <b> sa zhoduje s prvým <b>.
  2. .* začne tým, že skonzumuje celý zvyšok reťazca — všetko až do konca, vrátane druhého <b>World</b>.
  3. Engine potom potrebuje nájsť </b>, aby zhodu dokončil, a tak začne od konca .* ustupovať po jednom znaku.
  4. Prvé miesto (pri prehľadávaní odzadu), kde </b> sedí, je úplne posledné </b> v reťazci — nie to prvé, pri ktorom by „logicky“ mal skončiť.

Výsledná zhoda je teda celé <b>Hello</b> and <b>World</b>, pretože greedy párovanie vždy skúša najprv najdlhší možný reťazec a zmenšuje ho len o toľko, koľko je nutné, aby zvyšok vzoru uspel.

Oprava: urobte kvantifikátor lazy pomocou ?

Pridanie ? hneď za kvantifikátor ho prepne z greedy na lazy (nazýva sa aj „non-greedy“ alebo „reluctant“): *?, +?, ??, {n,m}?.

Lazy kvantifikátor robí opak: začne zhodou s čo najmenej znakmi a rozširuje sa len vtedy, keď zvyšok vzoru ešte nemôže uspieť.

<b>.*?</b> voči tomu istému reťazcu:

  1. <b> sa zhoduje s prvým <b>.
  2. .*? začne zhodou s nulou znakov.
  3. Engine skontroluje: zhoduje sa </b> presne tu? Nie (sme na „Hello…“, ešte nie na </b>) — takže rozšíri .*? presne o jeden znak a skontroluje znova.
  4. Toto sa opakuje znak po znaku, kým .*? neskonzumuje presne Hello, a v tom momente sa </b> okamžite zhoduje.

Výsledok: <b>Hello</b> a <b>World</b> sa vrátia ako dve samostatné zhody, čo je takmer vždy to, čo ste pri parsovaní štruktúr podobných značkám alebo oddeľovačom naozaj chceli.

Kedy greedy skutočne chcete (nie je to len „nesprávny default“)

Greedy nie je chyba jazyka — je správny pre iný, rovnako bežný prípad: zachytenie vonkajšej hranice niečoho, nie najmenšej jednotky vo vnútri. Ak extrahujete „všetko medzi prvou { a poslednou }“ v kuse textu podobného JSON-u (napríklad aby ste vytiahli celý objekt bez ohľadu na vnorenie), greedy je presne to pravé — lazy by sa zastavil pri prvej vnútornej } a dal by vám orezaný, neplatný fragment.

Pravidlo: lazy pre opakované malé ohraničené kúsky (značky, reťazce v úvodzovkách, položky zoznamov); greedy pre „zober celý vonkajší rozsah“.

Rýchly prehľad

Vzor Správanie Použite, keď
.*, .+, {n,m} Greedy — najdlhšia možná zhoda Chcete vonkajší rozsah, alebo je v reťazci len jedna zhoda
.*?, .+?, {n,m}? Lazy — najkratšia možná zhoda Máte viac podobných ohraničených kúskov a chcete každý samostatne

Ak si nie ste istí, čo vzor naozaj robí na vašom reálnom vstupe (a nie na testovacom reťazci), spustenie voči skutočnému textu s viacerými zhodami v regex testeri so živým zvýrazňovaním zhôd rozdiel okamžite zviditeľní — uvidíte jeden obrí zvýraznený blok pri greedy verzus niekoľko samostatných pri lazy, čo je zvyčajne rýchlejšie než premýšľanie znak po znaku.

← Späť na blog