
Regex mohó és lusta illesztés: miért markol túl sokat a mintád
Közzétéve 2026. júl. 24.
Megírod a <b>.*</b> mintát, hogy megragadd egy félkövér tag tartalmát, teszteled a <b>Hello</b> stringen, tökéletesen működik. Aztán valódi HTML-en futtatod, ahol két félkövér tag van egy sorban — <b>Hello</b> and <b>World</b> —, és két találat helyett egyetlen óriási találatot kapsz, amely mindkét taget átfogja. Ez nem a regexmotor hibája; ez a *, + és {n,m} alapértelmezett viselkedése, és egyetlen karakterrel javítható, ha egyszer érted, mi történik.
Miért markol többet a .*, mint várnád
A regexben a kvantorok alapértelmezetten mohók — a .* nem azt jelenti, hogy „illessz néhány karaktert“, hanem azt, hogy „illessz annyi karaktert, amennyit csak lehet, és csak akkor hátrálj, ha a minta többi része feltétlenül megköveteli“.
Járjuk végig lépésről lépésre a <b>.*</b> mintát a <b>Hello</b> and <b>World</b> stringen:
- A
<b>illeszkedik az első<b>-re. - A
.*azzal kezdi, hogy elfogyasztja a string teljes hátralévő részét — mindent a végéig, beleértve a második<b>World</b>-öt is. - A motornak ezután meg kell találnia a
</b>-t a találat befejezéséhez, így a.*végéről karakterenként kezd visszahátrálni. - Az első hely (a végétől visszafelé pásztázva), ahol a
</b>illeszkedik, a string legutolsó</b>-je — nem az első, ahol „logikusan“ meg kellene állnia.
Így a találat végül a teljes <b>Hello</b> and <b>World</b> lesz, mert a mohó illesztés mindig a lehető leghosszabb stringgel próbálkozik először, és csak annyit enged belőle, amennyi feltétlenül kell ahhoz, hogy a minta többi része sikerüljön.
A javítás: tedd lustává a kvantort a ?-lel
Egy ? közvetlenül a kvantor után mohóból lustává (más néven „nem mohóvá“ vagy „vonakodóvá“) változtatja: *?, +?, ??, {n,m}?.
A lusta kvantor az ellenkezőjét teszi: azzal kezdi, hogy a lehető legkevesebb karaktert illeszti, és csak akkor terjeszkedik, ha a minta többi része még nem tud sikerülni.
A <b>.*?</b> ugyanazon a stringen:
- A
<b>illeszkedik az első<b>-re. - A
.*?azzal kezdi, hogy nulla karaktert illeszt. - A motor ellenőrzi: illeszkedik-e itt rögtön a
</b>? Nem (a „Hello…“ résznél tartunk, még nem egy</b>-nél) — így a.*?-t pontosan egy karakterrel bővíti, és újra ellenőriz. - Ez karakterről karakterre ismétlődik, amíg a
.*?pontosan aHello-t fogyasztotta el; ekkor a</b>azonnal illeszkedik.
Eredmény: a <b>Hello</b> és a <b>World</b> két külön találatként jön vissza, ami szinte mindig az, amit valójában akartál tag-szerű vagy határolójel-szerű szerkezetek feldolgozásakor.
Amikor tényleg mohót akarsz (nem csupán „rossz alapértelmezés“)
A mohóság nem a nyelv hibája — egy másik, ugyanolyan gyakori esetre helyes: valaminek a legkülső határát akarod illeszteni, nem a legkisebb belső egységet. Ha egy JSON-szerű szövegblobból „mindent az első { és az utolsó } között“ akarsz kinyerni (mondjuk egy teljes objektumot, beágyazástól függetlenül), a mohó pontosan helyes, a lusta viszont az első belső }-nél állna meg, és csonka, érvénytelen töredéket adna.
A hüvelykujjszabály: lusta az ismétlődő, kis, határolt darabokhoz (tagek, idézőjeles stringek, listaelemek); mohó a „ragadd meg a teljes külső tartományt“ esethez.
Gyors áttekintés
| Minta | Viselkedés | Mikor használd |
|---|---|---|
.*, .+, {n,m} |
Mohó — a lehető leghosszabbat illeszti | A legkülső tartományt akarod, vagy csak egy találat van a stringben |
.*?, .+?, {n,m}? |
Lusta — a lehető legrövidebbet illeszti | Több hasonló, határolt darabod van, és mindet külön akarod |
Ha nem vagy biztos benne, egy minta valójában melyiket csinálja a valós bemeneteden a tesztstringed helyett, futtasd a tényleges, több találatot tartalmazó szövegen egy élő találatkiemelős regex-tesztelőben — a különbség azonnal láthatóvá válik: a mohónál egy óriási kiemelt blokkot látsz, a lustánál több különállót, ami általában gyorsabb, mint karakterenként végiggondolni.