Cheat Sheet Regex: Metakarakter, Grup, dan Lookaround (Referensi Lengkap)
Ekspresi reguler (regex) adalah bahasa pola mungil untuk mencocokkan teks. \d+ berarti “satu digit atau lebih”, ^Error berarti “baris yang diawali Error”. Cheat sheet ini meringkas seluruh sintaks dalam satu halaman: metakarakter, kuantifier, anchor, grup, lookaround, flag, plus 15+ pola yang bisa Anda tempel langsung ke JavaScript atau Python.
Halaman ini ditulis untuk developer yang sudah tahu apa itu string dan mencari referensi, bukan tur pengenalan. Lompat ke Tabel Referensi Cepat jika Anda hanya butuh simbolnya. Baca bagian lookaround dan jebakan jika Anda pernah punya regex yang menggantung server.
1. Apa itu regex dan kenapa Anda masih membutuhkannya di 2026
Regex adalah pola yang dikompilasi menjadi state machine, memindai sebuah string lalu cocok atau gagal. Tata bahasanya kecil, tetapi pemakaiannya tidak.
AI bisa menyusun draf pola untuk Anda, tetapi pekerjaan-pekerjaan ini tetap milik manusia yang menulis regex secara manual:
- Parsing log — Anda punya sepuluh juta baris log akses
nginxdan butuh setiap request 5xx dari user agent tertentu. Regex 40 karakter lewatgrep -Eselesai dalam hitungan detik; memanggil LLM per baris jelas tidak. - Validasi form dan field — nomor telepon, kode pos, ISO timestamp, kunci lisensi. Polanya hidup di sebelah input dan dijalankan setiap keystroke di browser.
- Find-and-replace massal — refactor seribu file di mana Anda perlu menangkap sebuah nama lalu menyuntikkannya kembali.
sed,ripgrep, dan fitur “Replace in files” editor Anda semuanya berbicara regex secara native.
Untuk paruh JSON dari toolbox yang sama, lihat cheat sheet jq command-line.
1.1 Cara membaca pola regex (tutorial regex 5 detik)
Sebagian besar pola lebih mudah dibaca dari kiri ke kanan, satu token sekali jalan. Ambil contoh ^[A-Z]\w+\d{2,4}$:
^mengikat match ke awal string.[A-Z]cocok dengan tepat satu huruf kapital.\w+cocok dengan satu karakter kata atau lebih.\d{2,4}cocok dengan dua sampai empat digit.$mengikat ke akhir string.
Trik utamanya: baca anchor lebih dulu, lalu kelas karakter, baru kuantifier.
2. Tabel referensi cepat
Bagian yang dicari kebanyakan pembaca. Salin yang Anda butuhkan.
Metakarakter
| Pola | Cocok dengan |
|---|---|
. | Karakter apa pun kecuali newline (atau karakter apa pun dengan flag s/dotall) |
\d | Sebuah digit ([0-9], atau semua digit Unicode dengan flag u) |
\D | Bukan digit |
\w | Karakter kata ([A-Za-z0-9_]) |
\W | Bukan karakter kata |
\s | Whitespace apa pun (spasi, tab, newline, …) |
\S | Bukan whitespace |
Kuantifier
| Pola | Cocok dengan |
|---|---|
* | 0 kali atau lebih (greedy) |
+ | 1 kali atau lebih (greedy) |
? | 0 atau 1 kali (greedy) |
{n} | Tepat n kali |
{n,m} | Antara n dan m kali |
{n,} | n kali atau lebih |
*?, +?, ??, {n,m}? | Versi lazy dari masing-masing kuantifier |
Anchor
| Pola | Cocok dengan |
|---|---|
^ | Awal string (atau awal baris dengan flag m) |
$ | Akhir string (atau akhir baris dengan flag m) |
\b | Batas kata (word boundary) |
\B | Bukan batas kata |
\A | Awal absolut dari string (Python) |
\Z | Akhir absolut dari string (Python) |
Kelas karakter
| Pola | Cocok dengan |
|---|---|
[abc] | Salah satu dari a, b, c |
[^abc] | Apa pun kecuali a, b, c |
[a-z] | Huruf kecil apa saja |
[0-9] | Digit apa saja |
\p{L} | Huruf Unicode apa saja (flag u di JS, default di Python re) |
Grup
| Pola | Cocok dengan |
|---|---|
(...) | Capture group |
(?:...) | Non-capture group |
(?<name>...) | Named capture (JS ES2018+); Python pakai (?P<name>...) |
\1, \2 | Backreference ke grup 1, 2 |
Lookaround
| Pola | Cocok dengan |
|---|---|
(?=...) | Positive lookahead |
(?!...) | Negative lookahead |
(?<=...) | Positive lookbehind |
(?<!...) | Negative lookbehind |
Flag
| Flag | Efek |
|---|---|
i | Case-insensitive |
m | Multiline: ^ dan $ cocok per baris |
s | Dotall: . cocok dengan newline |
g | Global (JS) — temukan semua match |
u | Mode Unicode |
y | Sticky (JS) — anchor ke lastIndex |
3. Metakarakter dan kelas karakter
3.1 Literal vs karakter spesial
Sebagian besar karakter bersifat literal. Ada 12 metakarakter yang harus di-escape jika Anda ingin memperlakukannya sebagai dirinya sendiri:
. ^ $ * + ? ( ) [ ] { } | \
Lupa meng-escape . adalah bug regex yang paling sering muncul. \. cocok dengan titik literal. Di dalam kelas karakter, [.] juga cocok dengan titik literal. Kebanyakan metakarakter kehilangan kekuatannya di dalam [...], kecuali ], \, ^ (saat di posisi pertama), dan - (di tengah).
3.2 Shorthand karakter
Kelas shorthand kelihatan sederhana sampai Unicode muncul:
// JavaScript — tanpa flag u, \d hanya ASCII
/\d/.test('5'); // true
/\d/.test('٥'); // false (digit Arab-Indic)
/\d/u.test('٥'); // false — bahkan dengan u, \d tetap ASCII di JS
/\p{N}/u.test('٥'); // true — \p{N} adalah kelas digit yang Unicode-aware
# Python — modul re memperlakukan \d sebagai Unicode secara default
import re
re.match(r'\d', '٥') # <Match span=(0, 1)>
re.match(r'(?a)\d', '٥') # None — (?a) memaksa ASCII
Jika Anda hanya berurusan dengan input ASCII Inggris, \d dan [0-9] bisa dipertukarkan. Begitu pengguna menempelkan nama dengan aksen, pakai \p{L}, bukan \w.
3.3 Kelas karakter kustom
// JavaScript
/[A-Za-z][A-Za-z0-9_-]{2,29}/.test('valid_handle-1'); // true
// Negasi dan range digabung
/[^aeiou\s]/g // karakter apa pun yang bukan vokal dan bukan whitespace
Untuk kategori Unicode, \p{L} artinya “huruf apa pun”, \p{N} artinya “angka apa pun”, \p{Script=Han} artinya “karakter Han apa pun”. JavaScript butuh flag u. Python hanya mendukung \p{...} lewat paket PyPI regex, bukan re di stdlib.
Kalau Anda kerja di command line, Anda juga akan ketemu kelas karakter POSIX:
| Kelas POSIX | Cocok dengan | Padanan ASCII |
|---|---|---|
[[:alpha:]] | huruf | [A-Za-z] |
[[:digit:]] | digit | [0-9] (\d) |
[[:alnum:]] | huruf + digit | [A-Za-z0-9] |
[[:space:]] | whitespace | \s |
[[:upper:]] | huruf besar | [A-Z] |
[[:lower:]] | huruf kecil | [a-z] |
Kelas POSIX bekerja di grep -E dan sed -E. Mereka tidak bekerja di JavaScript atau Python re — pakai \d, \s, \w sebagai gantinya.
4. Kuantifier dan greedy vs lazy
4.1 Kuantifier dasar
/a*/.exec('aaab') // ['aaa'] — 0 atau lebih
/a+/.exec('aaab') // ['aaa'] — 1 atau lebih
/a?/.exec('aaab') // ['a'] — 0 atau 1
/a{2,3}/.exec('aaaab') // ['aaa'] — 2 sampai 3
4.2 Greedy vs lazy
Secara default kuantifier bersifat greedy: ia melahap sebanyak mungkin, lalu mundur agar keseluruhan pola tetap cocok. Tambahkan ? untuk membalik perilaku itu jadi lazy.
const html = '<p>one</p><p>two</p>';
html.match(/<p>.*<\/p>/)[0]; // '<p>one</p><p>two</p>' (greedy melahap keduanya)
html.match(/<p>.*?<\/p>/)[0]; // '<p>one</p>' (lazy berhenti di yang pertama)
Versi lazy hampir selalu yang Anda inginkan ketika mengekstrak tag atau string ber-quote. Lebih bagus lagi, hindari . sama sekali dan pakai kelas yang dinegasi: <p>[^<]*</p> lebih cepat daripada <p>.*?</p> karena engine tidak perlu backtrack sama sekali.
4.3 Catastrophic backtracking
Beginilah cara regex menggantung server. Sarangkan kuantifier di dalam kuantifier lain dengan overlap yang ambigu, dan engine akan menelusuri jalur dalam jumlah eksponensial sebelum menyerah.
// Jangan lakukan ini
/(a+)+b/.test('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!'); // butuh beberapa detik
Untuk 41 a diikuti !, engine mencoba kira-kira 2^41 titik pemisahan sebelum memutuskan bahwa b tidak ada. Ada tiga jalan keluar:
- Ratakan polanya:
/a+b/menyelesaikan tugas yang sama tanpa nesting. - Pakai atomic group (Python
regex, PCRE, Java, Ruby):(?>a+)+b. Begitua+cocok, engine menolak backtrack ke dalamnya. - Ganti engine:
regexpmilik Go, RE2, dan crateregexRust menggunakan NFA linear-time dan secara desain tidak bisa catastrophic backtracking.
JavaScript dan re Python sama-sama melakukan backtracking dan tidak punya atomic group di stdlib (paket PyPI regex di Python menambahkannya). Saat Anda mengontrol panjang input, ini tidak masalah. Saat input datang dari pengguna, validasi panjangnya dulu atau kompilasi ulang lewat RE2.
5. Anchor dan word boundary
5.1 ^ dan $
Secara default, ^ adalah awal dari keseluruhan input dan $ adalah akhirnya. Dengan flag m (multiline), keduanya menjadi awal dan akhir setiap baris:
const log = 'INFO start\nERROR boom\nINFO done';
log.match(/^ERROR.*/); // null — mode satu baris, ^ hanya cocok di index 0
log.match(/^ERROR.*/m); // ['ERROR boom']
5.2 \b dan \B
\b adalah zero-width assertion: ia cocok dengan posisi di antara karakter kata (\w) dan karakter bukan-kata. Berguna untuk pencarian seluruh kata:
/\bcat\b/.test('the cat sat'); // true
/\bcat\b/.test('concatenate'); // false
Batas kata didefinisikan berdasarkan \w, yang secara default adalah ASCII. Teks Tionghoa, Jepang, dan Korea tidak memakai spasi antar kata, jadi \b tidak mendeteksi tepi kata di sana. Anda butuh tokenizer (jieba, MeCab) sebelum regex, bukan sebagai pengganti regex.
5.3 Mode multiline
import re
text = "INFO ok\nERROR fail\nINFO done\n"
re.findall(r'^ERROR.*$', text) # []
re.findall(r'^ERROR.*$', text, re.MULTILINE) # ['ERROR fail']
Di JavaScript bentuk yang sama dibaca text.match(/^ERROR.*$/gm). Kombinasikan m dengan g untuk mengambil setiap baris yang cocok.
6. Grup, capture, dan backreference
6.1 Capturing group
Tanda kurung menjalankan dua tugas: mengelompokkan sub-pola untuk kuantifier, dan meng-capture match supaya bisa dipakai nanti.
'2026-05-13'.match(/(\d{4})-(\d{2})-(\d{2})/);
// ['2026-05-13', '2026', '05', '13', index: 0, ...]
Grup dinomori dari kiri ke kanan berdasarkan kurung pembukanya, dimulai dari 1.
6.2 Non-capturing group
Kalau Anda hanya butuh pengelompokan, bukan capture, pakai (?:...). Lebih cepat, dan grup bernomor tetap rapi:
/(?:https?):\/\/(\S+)/.exec('see https://go-tools.org');
// ['https://go-tools.org', 'go-tools.org']
// — protokol dikelompokkan tapi tidak di-capture; grup 1 adalah host
6.3 Named group
Menamai grup membuat pola lebih mudah dibaca dan aman untuk refactor.
// JavaScript (ES2018+)
const m = '2026-05-13'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/);
m.groups.year; // '2026'
# Python — perhatikan sintaks (?P<...>)
import re
m = re.match(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', '2026-05-13')
m.group('year') # '2026'
6.4 Backreference
Backreference membuat bagian pola yang lebih belakang bisa mengulang apa yang sudah di-capture sebelumnya.
// Temukan karakter apa pun yang berulang berturut-turut
'bookkeeper'.match(/(\w)\1/g); // ['oo', 'kk', 'ee']
// Cocokkan pasangan tag HTML berdasarkan nama
const tag = /<(\w+)>(.*?)<\/\1>/;
'<b>bold</b>'.match(tag);
// ['<b>bold</b>', 'b', 'bold']
Di Python, \1 bekerja baik di pola maupun di replacement. Named reference dibaca (?P=name) di pola dan \g<name> di replacement re.sub.
7. Lookaround: lookahead dan lookbehind
Lookaround adalah zero-width assertion. Ia memeriksa kondisi tanpa mengonsumsi karakter, jadi Anda bisa merangkainya.
7.1 Lookahead
// Password: minimal 8 karakter, satu digit, satu huruf besar, satu huruf kecil
const strong = /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z]).{8,}$/;
strong.test('Hunter2!'); // true
strong.test('hunter2!'); // false — tidak ada huruf besar
// Negative lookahead — nama file yang bukan .tmp
/^[\w-]+(?!\.tmp$)\.[a-z]+$/.test('report.csv'); // true
7.2 Lookbehind
Lookbehind adalah cermin baliknya: ia menegaskan apa yang ada sebelum posisi sekarang.
// Ekstrak harga setelah simbol mata uang — simpan angkanya, buang $
'price: $42.50'.match(/(?<=\$)\d+(\.\d+)?/); // ['42.50', '.50']
// Negative lookbehind — cocok dengan Bond tapi bukan James Bond
'Mr. Bond'.match(/(?<!James )Bond/); // ['Bond']
'James Bond'.match(/(?<!James )Bond/); // null
7.3 Lookbehind JavaScript vs Python
Salah satu dari sedikit tempat di mana kedua engine berbeda cukup jauh sampai pola bisa rusak ketika Anda mem-port-nya.
| Engine | Panjang lookbehind |
|---|---|
| JavaScript (V8, SpiderMonkey, JSC 16.4+) | Variable-width sejak ES2018. (?<=\d+) valid. |
Python stdlib re | Fixed-width saja. (?<=\d+) memunculkan error: look-behind requires fixed-width pattern. |
Paket PyPI regex Python | Mendukung variable-width. import regex; regex.search(r'(?<=\d+)abc', '12abc'). |
Workaround di Python: tulis ulang lookbehind dengan repeat yang panjangnya tetap ((?<=\d{3})), atau capture prefix-nya lalu potong setelah match.
8. Flag dan modifier
8.1 i — case-insensitive
/error/i.test('FATAL ERROR'); // true
re.search(r'error', 'FATAL ERROR', re.IGNORECASE) # <Match span=(6, 11)>
8.2 m dan s
m mengubah ^ dan $ menjadi anchor per baris. s (dotall) membuat . bisa cocok dengan newline. Keduanya independen, kombinasikan saat Anda butuh keduanya.
/<script>(.*?)<\/script>/s.exec('<script>\nalert(1)\n</script>')[1];
// '\nalert(1)\n' — tanpa s, . akan menolak newline
8.3 g — global
Di JavaScript, g mengubah API-nya, bukan match-nya. Tanpa g, String.match mengembalikan capture group. Dengan g, ia mengembalikan setiap string yang match. Pakai matchAll jika ingin mempertahankan capture group di seluruh match.
const text = 'a=1 b=2 c=3';
text.match(/(\w)=(\d)/); // match pertama dengan grup
text.match(/(\w)=(\d)/g); // ['a=1', 'b=2', 'c=3'] — tanpa grup
[...text.matchAll(/(\w)=(\d)/g)]; // setiap match, dengan grup
Python tidak memakai g — re.findall, re.finditer, dan re.sub adalah varian globalnya.
8.4 u — Unicode dan \p{...}
// Cocokkan karakter Han apa pun (Tionghoa, kanji Jepang)
/\p{Script=Han}+/gu.test('Hello 世界'); // true
// Cocokkan emoji (extended pictographic)
/\p{Extended_Pictographic}/u.test('👋'); // true
Di Python, Unicode aktif secara default. re.findall(r'[一-鿿]+', text) adalah padanan untuk rentang Han. Untuk Unicode property escape penuh, pakai paket PyPI regex: regex.findall(r'\p{Script=Han}+', text).
9. Pola umum yang akan Anda pakai setiap hari
9.1 Validasi email
Tentukan dulu versi mana yang Anda butuhkan.
// Pola 95% — yang dipakai sebagian besar validator form
const email = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
email.test('a@b.co'); // true
// Pola "saya benar-benar ingin mendekati RFC 5322"
const rfc = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
Kenyataannya: validasi email RFC 5322 penuh dengan regex murni panjangnya ~6000 karakter dan masih salah di banyak edge case. Pakai pola 95%, lalu kirim email verifikasi. Itu satu-satunya tes yang benar-benar bekerja.
9.2 Ekstraksi URL
const urlPattern = /https?:\/\/[^\s<>"]+/g;
const found = 'See https://example.com/a?b=1 and http://x.io'.match(urlPattern);
// ['https://example.com/a?b=1', 'http://x.io']
Setelah Anda mengekstrak URL, biasanya Anda ingin memeriksa query string-nya. Tempel ke URL decoder/encoder kami, lalu parameter percent-encoded bisa dibaca sekilas. Untuk gambaran utuh kapan harus encode versus decode, baca panduan URL encoding & decoding.
9.3 Nomor telepon
// E.164 — internasional, + opsional dan kode negara 1-3 digit
const e164 = /^\+?[1-9]\d{1,14}$/;
e164.test('+14155551234'); // true
// North American Number Plan dengan separator
const nanp = /^(\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]?\d{3}[-.\s]?\d{4}$/;
nanp.test('(415) 555-1234'); // true
Untuk apa pun yang lebih dari sekadar “apakah bentuknya masuk akal”, pakai libphonenumber. Regex tidak bisa memvalidasi bahwa sebuah kode area benar-benar ada.
9.4 IPv4 dan IPv6
// IPv4 — ketat 0-255 per oktet
const ipv4 = /^((25[0-5]|2[0-4]\d|1?\d?\d)\.){3}(25[0-5]|2[0-4]\d|1?\d?\d)$/;
ipv4.test('192.168.1.1'); // true
ipv4.test('999.0.0.1'); // false
// IPv6 — bentuk yang disederhanakan. Pola RFC 4291 penuh ~600 karakter.
const ipv6simple = /^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$/;
ipv6simple.test('2001:0db8:85a3:0000:0000:8a2e:0370:7334'); // true
Untuk IPv6 sesungguhnya dengan shorthand ::, embedded IPv4, dan zone identifier, pakai isIP() dari node:net atau ipaddress.ip_address() di Python. Menulis ulang itu semua dengan regex murni adalah ritus inisiasi, lalu jadi beban pemeliharaan.
9.5 Tanggal dan timestamp ISO 8601
// Tanggal saja — YYYY-MM-DD
const isoDate = /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/;
isoDate.test('2026-05-13'); // true
// Tanggal + waktu + pecahan detik opsional + Z atau offset
const iso = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/;
iso.test('2026-05-13T09:30:00.123Z'); // true
ISO 8601 kelihatan sederhana tetapi penuh jebakan: leap second, week date (2026-W19), ordinal date (2026-133). Untuk epoch second versus milidetik dan pergeseran timezone, lihat panduan Unix timestamp.
10. Workflow find/replace dengan regex
10.1 JavaScript — String.replace dengan $1
// Format ulang tanggal AS: MM/DD/YYYY -> YYYY-MM-DD
'05/13/2026'.replace(/(\d{2})\/(\d{2})\/(\d{4})/, '$3-$1-$2');
// '2026-05-13'
// Pakai callback ketika replacement bersifat kondisional
'price 42 dollars'.replace(/(\d+) dollars/, (_, n) => `$${n}`);
// 'price $42'
$1, $2, … merujuk ke grup bernomor. $<name> merujuk ke named group. $& adalah match utuh. $$ adalah $ literal.
10.2 Python — re.sub dengan \1 dan callback
import re
# Format ulang tanggal sama seperti di atas
re.sub(r'(\d{2})/(\d{2})/(\d{4})', r'\3-\1-\2', '05/13/2026')
# '2026-05-13'
# Callback — kapitalisasi setiap alamat email dalam string
def upper_email(m):
return m.group(0).upper()
re.sub(r'[\w.-]+@[\w.-]+', upper_email, 'mail me at hi@go-tools.org')
# 'mail me at HI@GO-TOOLS.ORG'
Di replacement, Python memakai \1 atau \g<name>. Prefix raw string r'...' penting; tanpa itu \1 jadi karakter literal.
10.3 CLI: sed, grep, ripgrep, jq
Untuk refactor batch di command line, regex berpindah dari skrip ke shell:
# ripgrep — temukan setiap TODO dengan nama terlampir
rg -n '\bTODO\(([^)]+)\)' --replace 'TODO(\1)'
# grep -E dengan anchor — baris login gagal dari auth.log
grep -E '^[A-Z][a-z]{2} +[0-9]+ .*Failed password' /var/log/auth.log
# sed — buang trailing whitespace, in-place, di seluruh pohon direktori
find . -name '*.md' -print0 | xargs -0 sed -i -E 's/[[:space:]]+$//'
ripgrep memakai crate regex Rust (gaya RE2, waktu linear, tanpa lookbehind). grep -E dan sed -E memakai POSIX extended regex, yang tidak punya \d: pakai [0-9] atau [[:digit:]] sebagai gantinya. Saat datanya adalah JSON, ganti regex dengan jq. Lihat cheat sheet jq untuk kartu referensi paralel.
11. Jebakan umum
11.1 Lupa meng-escape .
Bug nyata yang pernah kami rilis: redactor log yang seharusnya menutup alamat IP.
// Salah — juga cocok dengan '192a168b1c1'
/(\d+).(\d+).(\d+).(\d+)/.test('192a168b1c1'); // true
// Benar
/(\d+)\.(\d+)\.(\d+)\.(\d+)/.test('192a168b1c1'); // false
Di dalam kelas karakter, . sudah literal, jadi [.] dan \. sama-sama bekerja. Di luar kelas, escape-lah.
11.2 .* greedy melahap terlalu banyak
'<a href="x"><b>bold</b></a>'.match(/<(.*)>/)[1];
// 'a href="x"><b>bold</b></a' — semuanya!
.* greedy memindai sampai ujung string, lalu mundur sampai > cocok, yaitu > paling akhir di input. Pilih lazy (.*?), atau yang lebih cepat dan lebih jelas, pakai kelas yang dinegasi ([^>]*).
11.3 Anchor multiline
Kebingungan yang sering muncul: ^ dan $ tidak cocok dengan karakter newline secara default. Mereka cocok dengan posisi di awal dan akhir keseluruhan input. Flag m itulah yang mengubah keduanya jadi anchor per baris. Flag s itulah yang membiarkan . melintasi newline. Keduanya ortogonal, dan biasanya Anda butuh keduanya untuk parsing log.
11.4 ReDoS dan cara menjinakkannya
ReDoS, alias regex denial of service, adalah versi production-grade dari catastrophic backtracking. Solusinya:
- Analisis statis: tools seperti
safe-regex,recheck, danno-misleading-character-classmilik ESLint menangkap pola berbahaya sebelum dirilis. - Atomic group (Python
regex, PCRE, Ruby, Java):(?>...)mencegah engine masuk ulang ke grup ketika backtrack. - Possessive quantifier (
*+,++,?+di PCRE/Java): ide sama, sintaks lebih ringkas. - Pindah ke engine non-backtracking:
regexpGo, RE2, crateregexRust, dan binding Pythonre2semuanya berjalan dalam waktu linear. ripgrep adalah deployment RE2 terpopuler di alam liar. - Validasi panjang input lebih dulu. Regex bomb 10 KB adalah bug; cap 10 byte pada input cuma satu baris kode.
Untuk inventaris yang lebih luas dari tools harian yang berpasangan dengan regex (formatter, decoder, converter), lihat panduan developer tools.
Sebelum Anda rilis pola yang kompleks, uji secara interaktif. regex101.com berpindah antara flavor PCRE, JavaScript, Python, dan Go, menjelaskan setiap token, dan menampilkan backtracking sehingga Anda bisa mengendus pola yang berpotensi catastrophic.
12. FAQ
Apa perbedaan antara * dan + di regex?
* cocok dengan nol kemunculan atau lebih (bisa cocok dengan string kosong). + cocok dengan satu kemunculan atau lebih (butuh minimal satu). a* cocok dengan '', 'a', dan 'aaaa'. a+ cocok dengan 'a' dan 'aaaa', tetapi bukan ''.
Bagaimana cara mencocokkan di beberapa baris dengan regex?
Nyalakan flag multiline (/.../m di JavaScript, re.MULTILINE di Python) sehingga ^ dan $ melekat ke setiap baris. Supaya . juga melintasi newline, tambahkan flag dotall (s di JavaScript, re.DOTALL di Python).
Apakah regex sama di JavaScript dan Python?
Sintaks intinya (kuantifier, anchor, kelas karakter, grup dasar) 90% sama. Dua perbedaan nyata: JavaScript (ES2018+) mendukung lookbehind variable-length dan menulis named group sebagai (?<name>...). re stdlib Python butuh lookbehind fixed-width dan memakai (?P<name>...). Untuk lookbehind variable-length di Python, install paket regex dari PyPI.
Kenapa regex saya mengalami catastrophic backtracking?
Anda punya kuantifier bersarang dengan match yang tumpang tindih, misalnya (a+)+ atau (a|a)*. Pada input yang hampir cocok tetapi gagal di dekat akhir, engine mencoba setiap pembagian dari kuantifier dalam, dan jumlah jalurnya eksponensial. Perbaiki dengan atomic group (?>a+)+, possessive quantifier a++, atau pindah ke engine non-backtracking seperti RE2 atau regexp Go.
Bisakah saya memakai lookbehind di JavaScript?
Bisa. Positive (?<=...) dan negative (?<!...) lookbehind sudah ada di V8 (Chrome, Node.js), SpiderMonkey (Firefox), dan JavaScriptCore (Safari 16.4+) sejak ES2018. Lookbehind variable-length didukung. Untuk Safari yang lebih lama, transpile dengan Babel atau lakukan feature-detect dengan try/catch di sekitar new RegExp.
Bagaimana cara mencocokkan titik . literal di regex?
Escape dengan backslash: \. cocok dengan titik literal. Di dalam kelas karakter, titik sudah literal, jadi [.] dan [\.] sama-sama bekerja. Di luar kelas, . yang tidak di-escape adalah metakarakter yang berarti “karakter apa pun kecuali newline” (atau karakter apa pun jika flag dotall aktif).
Apa arti \s di regex?
\s cocok dengan karakter whitespace apa pun — spasi, tab, newline, carriage return. Di mode Unicode, ia juga cocok dengan NBSP. \S adalah kebalikannya.
Apakah regex case sensitive?
Secara default, ya. Pakai flag i di JavaScript (/cat/i) atau re.IGNORECASE / (?i) di Python.