Kompilasi sekali, lalu cocokkan
Tulis pola sebagai raw string literal di dalam backtick. Di string biasa yang memakai tanda kutip ganda, setiap backslash harus ditulis dua kali ("\\d+"), dan hasilnya cepat sekali sulit dibaca.
regexp.MustCompile memicu panic kalau polanya tidak valid, dan itulah yang kamu inginkan untuk pola di source code-mu: salah ketik langsung gagal saat startup, bukan pada request pertama. Untuk pola yang berasal dari pengguna atau konfigurasi, pakai regexp.Compile dan tangani error-nya:
re, err := regexp.Compile(userPattern)
if err != nil {
return fmt.Errorf("bad pattern: %w", err)
}
Kompilasi sekali saja. Kompilasi jauh lebih mahal daripada pencocokan. regexp.MustCompile di dalam fungsi yang berjalan per request atau per baris akan mengulang pekerjaan itu setiap kali. Taruh *regexp.Regexp yang sudah dikompilasi di var level package atau di field struct. Regexp yang sudah dikompilasi aman dipakai oleh banyak goroutine sekaligus.
regexp.MatchString(pattern, s) mengompilasi dan mencocokkan dalam satu pemanggilan. Ini tidak masalah untuk pemeriksaan sekali jalan, tapi boros di dalam perulangan.
Mencari kecocokan
Nama-nama method-nya mengikuti pola: Find + All? + String? + Submatch? + Index?.
| Bagian | Arti |
|---|---|
All | setiap kecocokan yang tidak tumpang tindih; menerima batas n (-1 untuk semuanya) |
String | bekerja pada string; tanpa bagian ini, method menerima dan mengembalikan []byte |
Submatch | juga mengembalikan capture group |
Index | mengembalikan offset byte alih-alih teks |
FindString mengembalikan "" saat tidak ada kecocokan, yang tidak bisa dibedakan dari kecocokan dengan string kosong. Kalau polanya bisa cocok dengan teks kosong, pakai FindStringIndex (mengembalikan nil kalau tidak cocok) atau MatchString lebih dulu. Varian All mengembalikan nil saat tidak ada yang cocok.
Capture group
Tanda kurung melakukan capture. FindStringSubmatch mengembalikan seluruh kecocokan di indeks 0, lalu satu entri untuk setiap group:
Selalu periksa nil sebelum mengakses hasilnya dengan indeks; kalau tidak ada yang cocok, m[1] akan panic.
(?:...) mengelompokkan tanpa capture, untuk alternasi atau pengulangan: (?:ab)+. Named group ditulis (?P<name>...), dan sejak Go 1.22 juga (?<name>...).
Mengganti teks
Jebakan klasik pada referensi group: "$1x" dibaca sebagai group bernama 1x, yang tidak ada, sehingga diganti dengan string kosong. Tulis "${1}x".
Untuk string yang tetap, strings.ReplaceAll, strings.Contains, dan strings.Split lebih sederhana dan lebih cepat daripada regexp. Pakai regexp saat teks yang kamu cari punya bentuk tertentu, bukan nilai yang tetap.
Ringkasan sintaks
Go memakai sintaks RE2, yang mencakup fitur bergaya Perl yang sudah familier:
| Sintaks | Cocok dengan |
|---|---|
. | karakter apa pun kecuali baris baru (dengan (?s), baris baru juga) |
\d \w \s | digit, karakter kata [0-9A-Za-z_], whitespace (hanya ASCII) |
\D \W \S | kebalikannya |
[abc] [^abc] [a-z] | character class |
\pL \p{Greek} | class Unicode: huruf apa pun, karakter Yunani apa pun |
* + ? {n,m} | pengulangan, greedy |
*? +? ?? | pengulangan, lazy |
^ $ | awal dan akhir teks (awal dan akhir baris, dengan (?m)) |
\b | batas kata (ASCII) |
a|b | alternasi |
(?i) | tidak membedakan huruf besar dan kecil dari titik itu sampai akhir group yang melingkupinya; biasanya ditulis di awal pola |
\d dan \w hanya cocok dengan ASCII. Untuk "huruf apa pun dalam bahasa apa pun", pakai \pL, dan untuk digit Unicode apa pun, \p{Nd}. regexp.QuoteMeta(s) meng-escape setiap karakter khusus di s, yang kamu butuhkan saat menyusun pola dari input pengguna.
Apa yang tidak bisa dilakukan RE2
Regexp di Go menjamin bahwa pencocokan berjalan dalam waktu linear terhadap panjang input. Fitur yang akan merusak jaminan itu tidak didukung:
- Tidak ada lookahead atau lookbehind:
(?=...),(?!...),(?<=...),(?<!...)adalah compile error. Karena Go 1.22 mulai menerima(?<name>...)untuk named group, lookbehind kini gagal dengan pesan yang membingungkan,invalid named capture, seperti yang ditunjukkan program di bawah. - Tidak ada backreference:
(\w)\1untuk mencocokkan huruf ganda tidak bisa dipakai. - Tidak ada possessive quantifier atau atomic group.
Solusinya hampir selalu sama: capture sedikit lebih banyak daripada yang kamu butuhkan, lalu filter atau potong di Go. Imbalannya, regexp Go tidak bisa didorong ke catastrophic backtracking. Pola seperti (a+)+$ yang bisa membekukan engine PCRE pada input pendek berjalan dalam waktu linear di sini, dan itu penting saat pola atau inputnya berasal dari luar.
Kesalahan umum
- Mengompilasi di dalam perulangan atau handler. Kompilasi sekali ke variabel level package.
- Pola dengan tanda kutip ganda.
"\d"bahkan bukan string Go yang valid; pakai backtick. - Lupa anchor saat validasi.
\d{5}cocok di dalam"abc123456xyz". Pakai^\d{5}$. - Mengakses hasil submatch dengan indeks tanpa pemeriksaan
nil. - Mengira
\watau\bmemahami teks non-ASCII. Pakai class Unicode seperti\pL. - Memakai regexp untuk string yang tetap. Package
stringslebih jelas dan lebih cepat.
Pertanyaan yang sering diajukan
Bagaimana cara memeriksa apakah sebuah string cocok dengan regex di Go?
Kompilasi pola sekali saja dengan regexp.MustCompile, tulis sebagai raw string di dalam backtick supaya backslash tidak perlu di-escape, lalu panggil re.MatchString(s). Tanpa anchor (^ dan $), MatchString mengembalikan true kalau polanya cocok di bagian mana pun dari string.
Apakah regexp Go mendukung lookahead dan lookbehind?
Tidak. regexp di Go memakai sintaks RE2, yang tidak punya lookahead, lookbehind, atau backreference. Sebagai gantinya, pencocokan berjalan dalam waktu linear terhadap ukuran input, jadi pola atau input yang berbahaya tidak bisa membuat programmu macet. Pakai capture group dan sedikit kode Go, atau pecah pemeriksaannya menjadi dua regex.
Apa perbedaan regexp.Compile dan regexp.MustCompile?
Compile mengembalikan pola yang sudah dikompilasi dan sebuah error. MustCompile justru memicu panic kalau polanya tidak valid. Pakai MustCompile untuk pola yang ditulis di source code-mu, biasanya di variabel level package, supaya salah ketik langsung gagal saat startup. Pakai Compile untuk pola yang berasal dari pengguna atau file konfigurasi.
Bagaimana cara mengambil capture group dari regex di Go?
Pakai FindStringSubmatch, yang mengembalikan slice dengan indeks 0 berisi seluruh kecocokan dan indeks 1, 2, ... berisi group-nya, atau nil kalau tidak ada yang cocok. Untuk semua kecocokan, pakai FindAllStringSubmatch(s, -1). Named group (?P<year>\d{4}) bisa dicari dengan re.SubexpIndex("year").