Regex gak cuma ada di JavaScript. Python, PHP, dan Go juga punya, dan polanya hampir sama. Yang beda: cara membungkus pola, nama fungsinya, dan fitur yang tersedia. Bayangin resep nastar Bu Ratna dibawa ke tiga dapur. Resepnya sama, tapi tiap dapur punya cara menyalakan ovennya sendiri, dan satu dapur punya aturan: resep yang bisa gosong gak boleh masuk. Resep itu polanya, dapur itu bahasanya, dan dapur yang menolak resep berisiko itu Go: dia gak punya lookaround dan backreference, sebagai gantinya dijamin gak pernah kena ReDoS. Semua output di pelajaran ini hasil menjalankan kodenya sungguhan: Python 3.12, PHP 8.4, dan Go 1.27 (di Go Playground).
🧪 Tebakan cepat: di Python,
re.match(r"PO-\d{4}", chat)dijalankan pada chat PO kita, yang punyaPO-0412di baris pertama. Hasilnya kecocokanPO-0412, atauNone? Tebak dulu, jawabannya ada di bagian "Python: modulre".
Pola sama, pembungkus beda
Pola validasi tanggal ^(\d{4})-(\d{2})-(\d{2})$ di empat bahasa:
| Bahasa | Cara menulis | Flag, misalnya abaikan besar-kecil |
|---|---|---|
| JavaScript | /^(\d{4})-(\d{2})-(\d{2})$/.test(tanggal) |
sesudah garis miring: /.../i |
| Python | re.match(r"^(\d{4})-(\d{2})-(\d{2})quot;, tanggal) |
argumen: re.I |
| PHP | preg_match('/^(\d{4})-(\d{2})-(\d{2})$/', $tanggal) |
sesudah pembatas: '/.../i' |
| Go | regexp.MustCompile(`^(\d{4})-(\d{2})-(\d{2})
|
di dalam pola: (?i) |
- Pola intinya sama persis. Yang berubah cuma bungkusnya.
- Python memakai raw string
r"...": backslash dibiarkan apa adanya. Tanpar, Python 3.12 menulisSyntaxWarning: invalid escape sequence '\d'. - PHP menaruh pola di dalam string, diapit pembatas (delimiter): biasanya
/.../, bisa juga#...#atau~...~. Flag ditulis sesudah pembatas penutup. - Go memakai backtick
`...`, raw string milik Go. Di string kutip dua"...", Go menolak\dsaat compile.
Hasilnya sama di ketiga bahasa: ('2026', '03', '25') di Python, int(1) dari preg_match di PHP, dan [2026-03-25 2026 03 25] dari FindStringSubmatch di Go.
Python: modul re
import re
chat = open("chat.txt").read() # 6 baris chat PO Dapur Ratna
print(re.findall(r"PO-\d{4}", chat))
print(re.match(r"PO-\d{4}", chat))
print(re.search(r"PO-\d{4}", chat))
print(re.sub(r"(\d{2})/(\d{2})", r"\2-\1", "tgl 25/03"))
m = re.search(r"tgl (?P<hari>\d{2})/(?P<bulan>\d{2})", chat)
print(m.group("hari"), m.group("bulan"))
['PO-0412', 'PO-0413']
None
<re.Match object; span=(121, 128), match='PO-0412'>
tgl 03-25
25 03
re.findall()memberi semua kecocokan, sepertimatch()dengang. Tapi kalau polanya punya group, yang dia kembalikan isi group-nya:re.findall(r"([*_~])[^*_~]+\1", chat)memberi['*', '*', '*']. Butuh teks utuhnya?[m.group(0) for m in re.finditer(...)].re.match()cuma mencoba di awal teks, seolah ada^. Chat diawali[, jadi hasilnyaNone. Itu jawaban tebakannya.re.search()mencari di mana saja, danre.fullmatch()minta seluruh teks cocok.- Teks pengganti memakai
\2-\1(atau\g<2>), bukan$2-$1. Teks"$2-$1"ditulis apa adanya: hasilnyatgl $2-$1. - Named group ditulis
(?P<hari>...). Gaya JavaScript(?<hari>...)ditolak:re.error: unknown extension ?<h at position 5.
PHP: fungsi preg_*
<?php
$chat = file_get_contents('chat.txt'); // 6 baris chat PO Dapur Ratna
preg_match_all('/PO-\d{4}/', $chat, $m);
print_r($m[0]);
echo preg_replace('/(\d{2})\/(\d{2})/', '$2-$1', 'tgl 25/03'), "\n";
preg_match('/tgl (?<hari>\d{2})\/(?<bulan>\d{2})/', $chat, $t);
echo $t['hari'], ' ', $t['bulan'], "\n";
var_dump(preg_match('PO-\d{4}', $chat));
Array
(
[0] => PO-0412
[1] => PO-0413
)
tgl 03-25
25 03
Warning: preg_match(): Delimiter must not be alphanumeric, backslash, or NUL byte in …/po.php on line 9
bool(false)
- Pembatas wajib. Baris terakhir lupa pembatas, jadi PHP memberi Warning dan
false. - PHP gak punya flag
g. "Semua kecocokan" itu fungsinya sendiri:preg_match_all(). Menyalin'/PO-\d{4}/g'dari JavaScript memberiWarning: preg_match_all(): Unknown modifier 'g'. - Hasilnya masuk ke argumen ketiga (
$m):$m[0]semua kecocokan utuh,$m[1]isi group 1, dan seterusnya. Named group juga ada di$t['hari']. preg_replace()memakai$1,$2seperti JavaScript. Untuk function pengganti:preg_replace_callback().- Pola ditulis di kutip satu
'...', jadi\dsampai ke regex utuh.
Go: paket regexp (RE2)
re := regexp.MustCompile(`PO-\d{4}`)
fmt.Println(re.FindAllString(chat, -1))
tgl := regexp.MustCompile(`(\d{2})/(\d{2})`)
fmt.Println(tgl.ReplaceAllString("tgl 25/03", "${2}-${1}"))
fmt.Println(tgl.ReplaceAllString("tgl 25/03", "$2bln$1"))
_, err := regexp.Compile(`(?<=Rp)\d{1,3}(?:\.\d{3})+`)
fmt.Println(err)
_, err = regexp.Compile(`([*_~])[^*_~]+\1`)
fmt.Println(err)
[PO-0412 PO-0413]
tgl 03-25
tgl 25
error parsing regexp: invalid named capture: `(?<=Rp)\d{1,3}(?:\.\d{3})+`
error parsing regexp: invalid escape sequence: `\1`
FindAllString(teks, -1):-1artinya semua kecocokan.MustCompilelangsung berhenti (panic) kalau polanya salah;Compilemengembalikan error.- Di teks pengganti,
$2bln$1dibaca sebagai group bernama2bln, yang gak ada, jadi hasilnyatgl 25. Pakai kurung kurawal:${2}bln${1}. - Go mengikuti aturan RE2: gak ada lookahead, lookbehind, dan backreference. Pola yang memakainya ditolak saat compile. Go membaca
(?<sebagai awal nama group, makanya pesannya "invalid named capture". Tulis ulang: lookbehind diganti group biasa,Rp(\d{1,3}(?:\.\d{3})+)lalu ambil group 1; backreference diganti pilihan,\*[^*_~]+\*|_[^*_~]+_|~[^*_~]+~. - Imbalannya, dokumentasi Go menjamin waktunya linear terhadap panjang teks. Pola ReDoS
^(\w+\s?)+$dari pelajaran sebelumnya menjawabfalseuntuk 100.000 huruf diakhiri!tanpa macet. - Named group bisa
(?P<nama>...), dan sejak Go 1.22 juga(?<nama>...).
Tabel fitur
| Fitur | JavaScript | Python re |
PHP preg_* |
Go regexp |
|---|---|---|---|---|
| Lookahead dan lookbehind | ya | ya | ya | tidak |
Backreference \1 |
ya | ya | ya | tidak |
| Named group | (?<n>...) |
(?P<n>...) |
dua-duanya | dua-duanya |
| Teks pengganti | $1 |
\1 |
$1 |
${1} |
| Semua kecocokan | match(/.../g) |
findall() |
preg_match_all() |
FindAllString(s, -1) |
| Waktu dijamin linear | tidak | tidak | tidak | ya |
⚠️ Jebakan yang sering ketemu
re.matchdi Python terkunci di awal teks. Pakaire.searchuntuk mencari di mana saja.re.findalldengan group mengembalikan isi group, bukan teks utuhnya.- Pola PHP tanpa pembatas, atau dengan flag
g.'PO-\d{4}'dan'/PO-\d{4}/g'sama-sama berakhir dengan Warning. $1di Python ditulis apa adanya. Python memakai\1.- Lookbehind atau
\1dibawa ke Go ditolak saat compile. Tulis ulang dengan group atau pilihan|. - Pola di string biasa.
"\d"membuat Python memberi SyntaxWarning dan Go menolak compile. Pakair"..."di Python dan backtick di Go.
TL;DR: Polanya sama, bungkusnya beda: JavaScript /pola/flags, Python r"pola" dengan fungsi re, PHP '/pola/flags' dengan pembatas wajib (/, #, atau ~) dan fungsi preg_*, Go `pola` dengan paket regexp. Di Python, re.match terkunci di awal teks (chat yang diawali [ memberi None), sedangkan re.search mencari di mana saja. PHP gak punya flag g: pakai preg_match_all(). Teks pengganti $1 di JavaScript dan PHP, \1 di Python, ${1} di Go. Go memakai RE2: tanpa lookaround dan backreference, tapi waktunya dijamin linear, jadi kebal ReDoS.