Skip to content
Kembali ke Blog
Tutorial

Cheat Sheet Regex: Metakarakter, Grup, dan Lookaround

Cheat sheet regex lengkap: metakarakter, kuantifier, anchor, grup, lookaround, plus 15+ pola siap pakai untuk JavaScript dan Python. Termasuk cara menghindari catastrophic backtracking.

12 menit membaca

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 nginx dan butuh setiap request 5xx dari user agent tertentu. Regex 40 karakter lewat grep -E selesai 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

PolaCocok dengan
.Karakter apa pun kecuali newline (atau karakter apa pun dengan flag s/dotall)
\dSebuah digit ([0-9], atau semua digit Unicode dengan flag u)
\DBukan digit
\wKarakter kata ([A-Za-z0-9_])
\WBukan karakter kata
\sWhitespace apa pun (spasi, tab, newline, …)
\SBukan whitespace

Kuantifier

PolaCocok 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

PolaCocok dengan
^Awal string (atau awal baris dengan flag m)
$Akhir string (atau akhir baris dengan flag m)
\bBatas kata (word boundary)
\BBukan batas kata
\AAwal absolut dari string (Python)
\ZAkhir absolut dari string (Python)

Kelas karakter

PolaCocok 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

PolaCocok dengan
(...)Capture group
(?:...)Non-capture group
(?<name>...)Named capture (JS ES2018+); Python pakai (?P<name>...)
\1, \2Backreference ke grup 1, 2

Lookaround

PolaCocok dengan
(?=...)Positive lookahead
(?!...)Negative lookahead
(?<=...)Positive lookbehind
(?<!...)Negative lookbehind

Flag

FlagEfek
iCase-insensitive
mMultiline: ^ dan $ cocok per baris
sDotall: . cocok dengan newline
gGlobal (JS) — temukan semua match
uMode Unicode
ySticky (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 POSIXCocok denganPadanan 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:

  1. Ratakan polanya: /a+b/ menyelesaikan tugas yang sama tanpa nesting.
  2. Pakai atomic group (Python regex, PCRE, Java, Ruby): (?>a+)+b. Begitu a+ cocok, engine menolak backtrack ke dalamnya.
  3. Ganti engine: regexp milik Go, RE2, dan crate regex Rust 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.

EnginePanjang lookbehind
JavaScript (V8, SpiderMonkey, JSC 16.4+)Variable-width sejak ES2018. (?<=\d+) valid.
Python stdlib reFixed-width saja. (?<=\d+) memunculkan error: look-behind requires fixed-width pattern.
Paket PyPI regex PythonMendukung 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 gre.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:

  1. Analisis statis: tools seperti safe-regex, recheck, dan no-misleading-character-class milik ESLint menangkap pola berbahaya sebelum dirilis.
  2. Atomic group (Python regex, PCRE, Ruby, Java): (?>...) mencegah engine masuk ulang ke grup ketika backtrack.
  3. Possessive quantifier (*+, ++, ?+ di PCRE/Java): ide sama, sintaks lebih ringkas.
  4. Pindah ke engine non-backtracking: regexp Go, RE2, crate regex Rust, dan binding Python re2 semuanya berjalan dalam waktu linear. ripgrep adalah deployment RE2 terpopuler di alam liar.
  5. 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.

Artikel Terkait

Lihat semua artikel