Menu

Regexp di Golang: Match, Find, Submatch, dan Replace

Cara memakai regular expression di Go dengan package regexp: MustCompile, MatchString, FindString dan FindAllString, capture group dan named group, ReplaceAllString, serta batasan sintaks RE2 seperti tidak adanya lookbehind.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

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?.

BagianArti
Allsetiap kecocokan yang tidak tumpang tindih; menerima batas n (-1 untuk semuanya)
Stringbekerja pada string; tanpa bagian ini, method menerima dan mengembalikan []byte
Submatchjuga mengembalikan capture group
Indexmengembalikan 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:

SintaksCocok dengan
.karakter apa pun kecuali baris baru (dengan (?s), baris baru juga)
\d \w \sdigit, karakter kata [0-9A-Za-z_], whitespace (hanya ASCII)
\D \W \Skebalikannya
[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))
\bbatas kata (ASCII)
a|balternasi
(?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)\1 untuk 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 \w atau \b memahami teks non-ASCII. Pakai class Unicode seperti \pL.
  • Memakai regexp untuk string yang tetap. Package strings lebih 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").

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai