CodeKitHub
Bahasa Indonesia
Regex greedy vs lazy matching: kenapa pattern Anda menangkap terlalu banyak

Regex greedy vs lazy matching: kenapa pattern Anda menangkap terlalu banyak

Dipublikasikan 24 Jul 2026

Anda menulis <b>.*</b> untuk mengambil isi tag bold, mengujinya pada <b>Hello</b>, dan hasilnya sempurna. Lalu Anda jalankan pada HTML asli yang punya dua tag bold di baris yang sama — <b>Hello</b> and <b>World</b> — dan alih-alih mendapat dua match, Anda malah mendapat satu match raksasa yang membentang di kedua tag. Ini bukan bug pada regex engine Anda; ini adalah perilaku default dari *, +, dan {n,m}, dan solusinya cukup satu karakter begitu Anda paham apa yang sebenarnya terjadi.

Kenapa .* menangkap lebih dari yang diharapkan

Secara default, quantifier dalam regex bersifat greedy.* bukan berarti “cocokkan beberapa karakter,” melainkan “cocokkan sebanyak mungkin karakter, lalu baru mundur jika sisa pattern benar-benar mengharuskannya.”

Mari telusuri <b>.*</b> terhadap <b>Hello</b> and <b>World</b> langkah demi langkah:

  1. <b> cocok dengan <b> pertama.
  2. .* mulai dengan melahap seluruh sisa string — semuanya sampai akhir, termasuk <b>World</b> yang kedua.
  3. Engine kemudian perlu menemukan </b> untuk menyelesaikan match, jadi ia mulai mundur dari ujung .* satu karakter demi satu karakter.
  4. Posisi pertama (dihitung mundur dari akhir) di mana </b> cocok adalah </b> paling terakhir dalam string — bukan yang pertama yang “secara logis” seharusnya jadi titik berhenti.

Jadi hasil match-nya adalah <b>Hello</b> and <b>World</b> secara keseluruhan, karena greedy matching selalu mencoba string terpanjang terlebih dahulu dan hanya menyusutkannya sesedikit mungkin agar sisa pattern berhasil cocok.

Solusinya: buat quantifier menjadi lazy dengan ?

Menambahkan ? tepat setelah quantifier akan membalikkan sifatnya dari greedy menjadi lazy (juga disebut “non-greedy” atau “reluctant”): *?, +?, ??, {n,m}?.

Quantifier lazy melakukan kebalikannya: ia mulai dengan mencocokkan sesedikit mungkin karakter, lalu baru memperluas jika sisa pattern belum bisa cocok.

<b>.*?</b> terhadap string yang sama:

  1. <b> cocok dengan <b> pertama.
  2. .*? mulai dengan mencocokkan nol karakter.
  3. Engine memeriksa: apakah </b> cocok tepat di sini? Tidak (posisi masih di “Hello…”, belum sampai </b>) — jadi .*? diperluas satu karakter dan diperiksa lagi.
  4. Ini berulang karakter demi karakter sampai .*? sudah melahap tepat Hello, dan pada titik itu </b> langsung cocok.

Hasilnya: <b>Hello</b> dan <b>World</b> kembali sebagai dua match terpisah, yang hampir selalu itulah yang sebenarnya Anda inginkan saat mem-parsing struktur mirip tag atau delimiter.

Kapan Anda justru butuh greedy (bukan sekadar “default yang salah”)

Greedy bukan kesalahan dalam bahasa regex — ia benar untuk kasus lain yang sama seringnya terjadi: menangkap batas terluar dari sesuatu, bukan unit terkecil di dalamnya. Jika Anda mengekstrak “semua yang ada di antara { pertama dan } terakhir” dari teks mirip JSON (misalnya untuk mengambil satu object utuh terlepas dari nesting-nya), greedy adalah pilihan yang tepat, sementara lazy justru akan berhenti di } bagian dalam pertama dan menghasilkan potongan yang terpotong dan tidak valid.

Aturan praktisnya: lazy untuk potongan kecil berulang yang dibatasi delimiter (tag, string berkutip, item list); greedy untuk “ambil seluruh rentang terluar.”

Referensi cepat

Pattern Perilaku Gunakan saat
.*, .+, {n,m} Greedy — cocok dengan yang terpanjang Anda ingin rentang terluar, atau hanya ada satu match dalam string
.*?, .+?, {n,m}? Lazy — cocok dengan yang terpendek Anda punya beberapa potongan mirip yang dibatasi delimiter dan ingin masing-masing terpisah

Kalau Anda tidak yakin perilaku mana yang sebenarnya sedang dilakukan sebuah pattern pada input asli Anda (bukan string uji), menjalankannya pada teks multi-match yang sesungguhnya di regex tester dengan highlight match secara langsung akan langsung memperlihatkan perbedaannya — Anda akan melihat satu blok highlight raksasa untuk greedy versus beberapa blok terpisah untuk lazy, yang biasanya lebih cepat dibanding menalar karakter demi karakter.

← Kembali ke Blog