Memvalidasi bentuk alamat email, mengekstrak semua angka dari sebuah paragraf, mengganti pola berulang — tugas seperti ini tidak bisa diselesaikan strings.Contains yang hanya mencari teks harfiah. Regular expression (regex) adalah bahasa mini untuk mendeskripsikan pola teks, dan package regexp dari standard library menerjemahkan bahasa itu ke dalam Go. Pada chapter ini kita akan membaca anatomi pola, menjalankannya dengan MatchString, FindString, dan ReplaceAllString, menandai bagian pola dengan grup bernama, lalu menimbang kapan regex sebaiknya tidak dipakai sama sekali.

Pola Pertama: Kelas dan Kuantifier #

Sebuah pola tersusun dari kelas karakter dan pengulang. Tabel berikut merangkum yang paling sering dipakai:

PolaMaknaContoh kecocokan
[0-9]+satu atau lebih digit1024
\d / \Ddigit / bukan digit7
\w / \Wkarakter kata: huruf, digit, garis bawahgo_1
\s / \Swhitespace / bukan whitespacespasi
.satu karakter apa puna
^, $penjaga awal dan akhir teks—
+, *, ?1+ kali, 0+ kali, 0 atau 1 kali—

Kombinasi [0-9]+ berarti “satu digit atau lebih berjajar”. Pola \d maknanya sama dengan [0-9], dan \w setara [0-9A-Za-z_]. Tulis pola selalu dalam raw string literal (backtick) — dalam string kutip biasa, setiap backslash harus diket ganda sehingga \\d rawan salah ketik.

Menjalankan Pola #

Pola dikompilasi sekali menjadi objek *regexp.Regexp, lalu objek itu dipakai berulang kali:

go
re := regexp.MustCompile(`\d+`)
fmt.Println(re.FindAllString("go 1.27 rilis 2026", -1))

Output yang Dihasilkan:

text
[1 27 2026]

Empat method inti melayani kebutuhan yang berbeda: MatchString menjawab ya-tidak, FindString mengembalikan kecocokan pertama, FindAllString mengumpulkan seluruh kecocokan (angka -1 berarti tanpa batas), dan ReplaceAllString mengganti setiap kecocokan dengan teks pengganti. Seluruhnya bekerja pada string UTF-8 dan mendukung kelas Unicode seperti \p{L} untuk semua huruf.

Grup Bernama #

Bagian pola yang diapit tanda kurung membentuk grup. Grup bernama memakai sintaks (?P<nama>...) — pola (?P<kode>[A-Z]+)-(?P<angka>\d+) menangkap kode seperti GO dan angka seperti 2026 dari teks GO-2026:

go
m := pola.FindStringSubmatch("pesanan GO-2026 siap dikirim")

Nilai balik FindStringSubmatch adalah slice: indeks 0 berisi kecocokan penuh, indeks berikutnya berisi isi tiap grup. Nama grup dipetakan ke indeksnya lewat pola.SubexpIndex("kode"), sementara pola.SubexpNames() mendaftar seluruh nama grup secara berurutan. Pola tanpa nama tetap bisa ditangkap lewat posisi kurungnya.

MustCompile atau Compile #

regexp.MustCompile panik ketika pola salah — misalnya [a- yang kurang kurung tutup menghasilkan panic: regexp: Compile([a-): error parsing regexp: missing closing ]: [a-``. Panik itu justru berguna untuk pola tetap: kesalahan ketik langsung ketahuan saat program pertama kali dijalankan, bukan senyap di dalam alur data. Untuk pola yang datang dari luar (input pengguna, berkas konfigurasi), gunakan regexp.Compile yang mengembalikan pasangan (*Regexp, error) dan tangani error-nya seperti error biasa — program utuh di bawah mencontohkan keduanya.

Validasi Email Sederhana #

Pola ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ memvalidasi email dalam bentuk umum: nama akun, simbol @, domain, titik, dan akhiran minimal dua huruf. Pola ini cukup untuk menyaring salah ketik yang jelas seperti siti@@salah.

Batas: Kinerja dan Kapan strings Cukup #

Implementasi regexp Go berbasis automaton dan dijamin berjalan dalam waktu linear terhadap panjang input, sehingga bencana catastrophic backtracking (sumber serangan ReDoS di banyak pustaka regex lain) tidak terjadi. Jaminan itu bukan gratis: kompilasi pola, eksekusi automaton, dan alokasi slice hasil semuanya membuat regex jauh lebih lambat daripada pemanggilan strings setara.

Program Utuh #

Berkas regexp-demo.go merangkai seluruh materi menjadi satu program:

go
package main

import (
	"fmt"
	"regexp"
)

func main() {
	// MustCompile untuk pola tetap: galat pola terdeteksi saat program mulai.
	angka := regexp.MustCompile(`\d+`)

	teks := "Stok 1024 dan 77 unit diterima hari ini."
	fmt.Println("MatchString :", angka.MatchString(teks))
	fmt.Println("FindString  :", angka.FindString(teks))
	fmt.Println("FindAll     :", angka.FindAllString(teks, -1))
	fmt.Println("ReplaceAll  :", angka.ReplaceAllString(teks, "#N"))

	// Grup penamaan menandai bagian-bagian pola.
	pola := regexp.MustCompile(`(?P<kode>[A-Z]+)-(?P<angka>\d+)`)
	m := pola.FindStringSubmatch("pesanan GO-2026 siap dikirim")
	for _, nama := range pola.SubexpNames() {
		if nama != "" {
			fmt.Printf("grup %-6s = %s\n", nama, m[pola.SubexpIndex(nama)])
		}
	}

	// Validasi email sederhana (bukan standar penuh RFC 5322).
	email := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
	for _, e := range []string{"[email protected]", "siti@@salah"} {
		fmt.Printf("email %-18s valid: %v\n", e, email.MatchString(e))
	}

	// Compile untuk pola yang baru datang saat runtime.
	re, err := regexp.Compile(`[0-9]+`)
	if err != nil {
		fmt.Println("pola salah:", err)
		return
	}
	fmt.Println("Compile     :", re.MatchString("versi go 1.27"))
}
bash
go run regexp-demo.go

Output yang Dihasilkan:

text
MatchString : true
FindString  : 1024
FindAll     : [1024 77]
ReplaceAll  : Stok #N dan #N unit diterima hari ini.
grup kode   = GO
grup angka  = 2026
email [email protected]   valid: true
email siti@@salah        valid: false
Compile     : true

Mari kita telaah baris demi baris program di atas:

  1. Baris 10 (regexp.MustCompile): pola \d+ dikompilasi sekali di awal; galat pola akan memunculkan panic sebelum baris lain berjalan.
  2. Baris 13-16 (empat method inti): keberadaan angka dicek (true), kecocokan pertama diambil (1024), seluruh kecocokan dikumpulkan ([1024 77]), lalu tiap kecocokan diganti #N pada teks asal.
  3. Baris 19-25 (grup bernama): pola ber-grup kode dan angka dijalankan pada teks pesanan; loop melewati SubexpNames() dan mencetak isi tiap grup lewat SubexpIndex.
  4. Baris 28-31 (validasi email): dua alamat diuji dengan pola sederhana; [email protected] lolos, siti@@salah ditolak.
  5. Baris 34-39 (Compile): pola runtime dikompilasi dengan penanganan error eksplisit — jalur yang wajib dipakai jika pola bukan tulisan tangan Anda.

Regex menutup kotak peralatan teks kita: dari string immutable, UTF-8, strings, strconv, bytes, Builder, hingga parsing. Perjalanan berikutnya meninggalkan teks dan masuk ke wilayah memori — bagaimana Go menyimpan nilai, dan bagaimana pointer memberi akses ke lokasi penyimpanan itu di section Pointer dan Memory .

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

regexp-demo.go

Unduh berkas
go
package main

import (
	"fmt"
	"regexp"
)

func main() {
	// MustCompile untuk pola tetap: galat pola terdeteksi saat program mulai.
	angka := regexp.MustCompile(`\d+`)

	teks := "Stok 1024 dan 77 unit diterima hari ini."
	fmt.Println("MatchString :", angka.MatchString(teks))
	fmt.Println("FindString  :", angka.FindString(teks))
	fmt.Println("FindAll     :", angka.FindAllString(teks, -1))
	fmt.Println("ReplaceAll  :", angka.ReplaceAllString(teks, "#N"))

	// Grup penamaan menandai bagian-bagian pola.
	pola := regexp.MustCompile(`(?P<kode>[A-Z]+)-(?P<angka>\d+)`)
	m := pola.FindStringSubmatch("pesanan GO-2026 siap dikirim")
	for _, nama := range pola.SubexpNames() {
		if nama != "" {
			fmt.Printf("grup %-6s = %s\n", nama, m[pola.SubexpIndex(nama)])
		}
	}

	// Validasi email sederhana (bukan standar penuh RFC 5322).
	email := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
	for _, e := range []string{"[email protected]", "siti@@salah"} {
		fmt.Printf("email %-18s valid: %v\n", e, email.MatchString(e))
	}

	// Compile untuk pola yang baru datang saat runtime.
	re, err := regexp.Compile(`[0-9]+`)
	if err != nil {
		fmt.Println("pola salah:", err)
		return
	}
	fmt.Println("Compile     :", re.MatchString("versi go 1.27"))
}