CodeKitHub
Regex mohó és lusta illesztés: miért markol túl sokat a mintád

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:

  1. A <b> illeszkedik az első <b>-re.
  2. 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.
  3. 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.
  4. 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:

  1. A <b> illeszkedik az első <b>-re.
  2. A .*? azzal kezdi, hogy nulla karaktert illeszt.
  3. 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.
  4. Ez karakterről karakterre ismétlődik, amíg a .*? pontosan a Hello-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.

← Vissza a bloghoz