
Regex greedy lawan lazy: kenapa corak anda menangkap terlalu banyak
Diterbitkan pada 24 Jul 2026
Anda menulis <b>.*</b> untuk menangkap kandungan tag bold, mengujinya pada <b>Hello</b>, ia berfungsi dengan sempurna. Kemudian anda jalankannya pada HTML sebenar dengan dua tag bold pada baris yang sama — <b>Hello</b> and <b>World</b> — dan bukannya dua padanan, anda dapat satu padanan gergasi yang merangkumi kedua-dua tag. Ini bukan pepijat dalam enjin regex anda; ia adalah tingkah laku lalai *, +, dan {n,m}, dan ia mempunyai pembaikan satu-aksara sebaik sahaja anda tahu apa yang berlaku.
Kenapa .* menangkap lebih daripada yang anda jangka
Secara lalai, pengkuantiti dalam regex adalah greedy — .* bukan bermaksud “padankan beberapa aksara,” ia bermaksud “padankan sebanyak mungkin aksara, kemudian undur hanya jika baki corak benar-benar memerlukannya.”
Ikuti <b>.*</b> terhadap <b>Hello</b> and <b>World</b> langkah demi langkah:
<b>sepadan dengan<b>pertama..*bermula dengan menelan keseluruhan baki rentetan — segala-galanya hingga ke penghujung, termasuk<b>World</b>kedua.- Enjin kemudian perlu mencari
</b>untuk menyelesaikan padanan, jadi ia mula undur dari penghujung.*satu aksara pada satu masa. - Tempat pertama (mengimbas ke belakang dari penghujung) di mana
</b>sepadan ialah</b>yang paling akhir dalam rentetan itu — bukan yang pertama yang “secara logik” sepatutnya ia berhenti.
Jadi padanan itu berakhir menjadi <b>Hello</b> and <b>World</b> secara keseluruhan, kerana padanan greedy sentiasa cuba rentetan terpanjang yang mungkin dahulu dan hanya mengecilkannya sedikit sahaja seperlunya untuk membolehkan baki corak berjaya.
Pembaikan: jadikan pengkuantiti lazy dengan ?
Menambah ? sejurus selepas pengkuantiti menukarnya daripada greedy kepada lazy (juga dipanggil “non-greedy” atau “reluctant”): *?, +?, ??, {n,m}?.
Pengkuantiti lazy melakukan sebaliknya: ia bermula dengan memadankan sesedikit mungkin aksara, kemudian hanya berkembang jika baki corak belum boleh berjaya.
<b>.*?</b> terhadap rentetan yang sama:
<b>sepadan dengan<b>pertama..*?bermula dengan memadankan sifar aksara.- Enjin menyemak: adakah
</b>sepadan di sini? Tidak (kita berada pada “Hello…”, belum sampai pada</b>) — jadi ia mengembangkan.*?sebanyak tepat satu aksara dan menyemak semula. - Ini berulang aksara demi aksara sehingga
.*?telah menelan tepatHello, pada ketika itu</b>sepadan serta-merta.
Hasilnya: <b>Hello</b> dan <b>World</b> kembali sebagai dua padanan berasingan, yang hampir selalu apa yang sebenarnya anda mahukan semasa menghurai struktur seperti tag atau seperti pembatas (delimiter).
Bila anda sebenarnya mahu greedy (ia bukan sekadar “lalai yang salah”)
Greedy bukan kesilapan dalam bahasa itu — ia betul untuk kes lain yang sama biasa: memadankan sempadan terluar sesuatu, bukan unit terkecil di dalamnya. Jika anda mengekstrak “segala-galanya antara { pertama dan } terakhir” dalam blok teks seperti JSON (katakan, untuk menangkap keseluruhan objek tanpa mengira bersarang), greedy adalah tepat betul dan lazy akan berhenti pada } dalam yang pertama sebaliknya, memberikan anda serpihan terpotong yang tidak sah.
Petua ringkas: lazy untuk kepingan kecil berulang berpembatas (tag, rentetan bertanda petik, item senarai); greedy untuk “tangkap keseluruhan rentang luar.”
Rujukan pantas
| Corak | Tingkah laku | Guna apabila |
|---|---|---|
.*, .+, {n,m} |
Greedy — sepadan terpanjang yang mungkin | Anda mahu rentang terluar, atau hanya ada satu padanan dalam rentetan |
.*?, .+?, {n,m}? |
Lazy — sepadan terpendek yang mungkin | Anda mempunyai berbilang kepingan berpembatas serupa dan mahu setiap satu secara berasingan |
Jika anda tidak pasti yang mana satu sebenarnya dilakukan oleh corak anda pada input sebenar anda berbanding rentetan ujian anda, menjalankannya terhadap teks berbilang-padanan sebenar dalam regex tester dengan penyerlahan padanan langsung menjadikan perbezaan itu jelas serta-merta — anda akan lihat satu blok gergasi diserlahkan untuk greedy berbanding beberapa yang berasingan untuk lazy, yang biasanya lebih pantas daripada menaakul aksara demi aksara.