
Regexin ahne vs. laiska täsmäys: miksi kuvio nappaa liikaa
Julkaistu 24.7.2026
Kirjoitat <b>.*</b> napataksesi lihavoidun tagin sisällön, testaat sitä merkkijonolla <b>Hello</b>, ja se toimii täydellisesti. Sitten ajat sen oikealla HTML:llä, jossa on kaksi lihavointitagia samalla rivillä — <b>Hello</b> and <b>World</b> — ja kahden täsmäyksen sijaan saatkin yhden jättimäisen täsmäyksen, joka kattaa molemmat tagit. Tämä ei ole bugi regex-moottorissasi; se on merkkien *, + ja {n,m} oletuskäytös, ja siihen on yhden merkin korjaus, kun tiedät mistä on kyse.
Miksi .* nappaa enemmän kuin odotat
Oletuksena regexin kvantifioijat ovat ahneita (greedy) — .* ei tarkoita “täsmää joitain merkkejä”, vaan “täsmää mahdollisimman monta merkkiä ja perääntyy vasta, jos loppuosa kuviosta ehdottomasti sitä vaatii”.
Käydään läpi <b>.*</b> merkkijonolla <b>Hello</b> and <b>World</b> vaihe vaiheelta:
<b>täsmää ensimmäiseen<b>-tagiin..*alkaa nielemällä koko lopun merkkijonosta — kaiken loppuun asti, mukaan lukien toisen<b>World</b>-tagin.- Moottorin täytyy sitten löytää
</b>saadakseen täsmäyksen valmiiksi, joten se alkaa perääntyä.*:n lopusta yksi merkki kerrallaan. - Ensimmäinen paikka (lopusta taaksepäin selattaessa), johon
</b>sopii, on merkkijonon aivan viimeinen</b>— ei se, johon sen “loogisesti” pitäisi pysähtyä.
Näin täsmäykseksi tulee koko <b>Hello</b> and <b>World</b>, koska ahne täsmäys yrittää aina ensin pisintä mahdollista merkkijonoa ja kutistaa sitä vain sen verran kuin on välttämätöntä, jotta loppuosa kuviosta onnistuu.
Korjaus: tee kvantifioijasta laiska kysymysmerkillä
Kysymysmerkin ? lisääminen heti kvantifioijan jälkeen kääntää sen ahneesta laiskaksi (kutsutaan myös “ei-ahneeksi” tai “vastahakoiseksi”): *?, +?, ??, {n,m}?.
Laiska kvantifioija tekee päinvastoin: se aloittaa täsmäämällä mahdollisimman vähän merkkejä ja laajenee vasta, jos loppuosa kuviosta ei vielä voi onnistua.
<b>.*?</b> samalla merkkijonolla:
<b>täsmää ensimmäiseen<b>-tagiin..*?aloittaa täsmäämällä nolla merkkiä.- Moottori tarkistaa: täsmääkö
</b>juuri tässä kohdassa? Ei (olemme kohdassa “Hello…”, emme vielä</b>:ssä) — joten se laajentaa.*?:tä täsmälleen yhdellä merkillä ja tarkistaa uudelleen. - Tämä toistuu merkki kerrallaan, kunnes
.*?on nielaissut täsmälleen sananHello, jolloin</b>täsmää välittömästi.
Tulos: <b>Hello</b> ja <b>World</b> palautuvat kahtena erillisenä täsmäyksenä, mikä on lähes aina se, mitä oikeasti halusit jäsentäessäsi tagien tai erottimien kaltaisia rakenteita.
Milloin oikeasti haluat ahneen (se ei ole vain “väärä oletus”)
Ahneus ei ole virhe kielessä — se on oikea ratkaisu toisenlaisessa, yhtä yleisessä tilanteessa: kun täsmäät jonkin uloimman rajan, et pienintä sisällä olevaa yksikköä. Jos poimit “kaiken ensimmäisen {:n ja viimeisen }:n väliltä” JSON-tyyppisestä tekstimössöstä (esimerkiksi napataksesi koko objektin sisäkkäisyydestä riippumatta), ahne on juuri oikea valinta, ja laiska pysähtyisi sen sijaan ensimmäiseen sisäiseen }:hen, jolloin saisit katkaistun, virheellisen fragmentin.
Nyrkkisääntö: laiska toistuville pienille rajatuille palasille (tagit, lainausmerkeissä olevat merkkijonot, listan kohteet); ahne “nappaa koko ulompi jakso” -tilanteisiin.
Pikaopas
| Kuvio | Käytös | Käytä kun |
|---|---|---|
.*, .+, {n,m} |
Ahne — täsmää pisimmän mahdollisen | Haluat uloimman jakson, tai merkkijonossa on vain yksi täsmäys |
.*?, .+?, {n,m}? |
Laiska — täsmää lyhimmän mahdollisen | Sinulla on useita samankaltaisia rajattuja palasia ja haluat kunkin erikseen |
Jos et ole varma, kumpaa kuvio oikeasti tekee todellisella syötteelläsi testimerkkijonon sijaan, sen ajaminen oikeaa moniosumaista tekstiä vasten regex-testerissä elävällä täsmäyskorostuksella tekee erosta heti näkyvän — näet yhden jättimäisen korostetun lohkon ahneella versiolla ja useita erillisiä laiskalla, mikä on yleensä nopeampaa kuin asian pohtiminen merkki merkiltä.