Chapter Pengenalan Regular Expressions telah memperlihatkan kekuatan pola sederhana seperti r"\d+" untuk menemukan angka di dalam teks, tetapi kita masih memakai pola tersebut seperti mantra: belum dibedah dari apa saja pola regex tersusun. Pada chapter ini kita membongkar atom-atom pembangun pola — karakter literal, karakter bebas, shorthand classes, dan anchor — lalu merangkainya menjadi pola nyata untuk nomor HP dan alamat email.

Karakter Literal dan Karakter Bebas #

Sebagian besar karakter dalam pola regex cocok dengan dirinya sendiri: pola 0812 hanya cocok dengan teks 0812, huruf demi huruf. Karakter spesial pertama yang kita pelajari adalah titik (.) yang berperan sebagai wildcard: satu titik cocok dengan satu karakter apa pun kecuali baris baru.

Mekanisme di balik layar: mesin regex membaca string dari kiri ke kanan dan mencoba pola di setiap posisi awal yang mungkin. Saat mesin bertemu titik di dalam pola, mesin menerima karakter apa pun yang menempati posisi tersebut, lalu melanjutkan ke bagian pola berikutnya.

python
import re

daftar = ["0812-3456-7890", "0812.3456.7890", "0812 3456 7890"]
for nomor in daftar:
    print(nomor, "->", re.findall(r"0812.3456", nomor))

Output yang Dihasilkan:

text
0812-3456-7890 -> ['0812-3456']
0812.3456.7890 -> ['0812.3456']
0812 3456 7890 -> ['0812 3456']

Titik pada pola r"0812.3456" menyerap tanda hubung, titik, bahkan spasi dari data. Perilaku seluas ini cocok untuk pencarian kasar, tetapi berbahaya untuk validasi: nomor 0812x3456 pun akan diterima. Jika yang kita maksud adalah titik sesungguhnya, karakter titik harus di-escape dengan backslash menjadi \..

python
import re

daftar = ["0812-3456-7890", "0812.3456.7890", "0812 3456 7890"]
for nomor in daftar:
    print(nomor, "->", bool(re.search(r"0812\.3456", nomor)))

Output yang Dihasilkan:

text
0812-3456-7890 -> False
0812.3456.7890 -> True
0812 3456 7890 -> False

Prinsip yang sama berlaku untuk semua karakter spesial. Daftar karakter yang perlu diawali \ bila ingin dicocokkan secara literal adalah . ^ $ * + ? { } [ ] \ | ( ).

Shorthand Classes untuk Kelas Karakter Umum #

Menuliskan [0-9] setiap kali butuh digit membuat pola cepat penuh sesak. Regex menyediakan enam shorthand classes untuk kelas karakter yang paling sering dipakai:

ShorthandArtiSetara dengan
\dsatu digit[0-9]
\Dsatu karakter non-digit[^0-9]
\wsatu huruf, digit, atau underscore[a-zA-Z0-9_]
\Wkebalikan dari \w[^a-zA-Z0-9_]
\ssatu whitespace: spasi, tab, baris baru
\Skebalikan dari \s

Setiap shorthand mencocokkan tepat satu karakter, sama seperti [0-9]. Perhatikan pula bahwa \w tidak memuat tanda hubung: pola \w+ akan berhenti di karakter -.

python
import re

kode = "A7-X9_ 3"
print(re.findall(r"\d", kode))
print(re.findall(r"\D", kode))
print(re.findall(r"\w", kode))
print(re.findall(r"\W", kode))
print(re.findall(r"\s", kode))
print(re.findall(r"\S", kode))

Output yang Dihasilkan:

text
['7', '9', '3']
['A', '-', 'X', '_', ' ']
['A', '7', 'X', '9', '_', '3']
['-', ' ']
[' ']
['A', '7', '-', 'X', '9', '_', '3']

Baris \W memperlihatkan tanda hubung dan spasi ditangkap karena keduanya bukan anggota \w. Sebaliknya, underscore ikut dalam hasil \w meskipun secara visual tampak seperti tanda baca.

Menambatkan Pola dengan ^ dan $ #

^ dan $ adalah anchor: penanda posisi yang tidak mengonsumsi karakter apa pun. ^ hanya cocok di posisi awal string, $ hanya di posisi akhir string. Tanpa anchor, pola boleh menempel di mana saja; dengan anchor, kita memaksa pola berada di tepi.

python
import re

pesan = "PENTING: rapat pukul 09.00. Jangan terlambat. PENTING"
print(re.findall(r"^PENTING", pesan))
print(re.findall(r"PENTING$", pesan))
print(bool(re.search(r"^rapat", pesan)))

Output yang Dihasilkan:

text
['PENTING']
['PENTING']
False

Kata PENTING muncul dua kali, tetapi ^PENTING hanya menemukan kemunculan pertama karena hanya kemunculan itu yang berada di awal string. Sebaliknya, re.search(r"^rapat", pesan) mengembalikan None — kata rapat berada di tengah string — sehingga bool() mencetaknya sebagai False.

Pola Nyata: Nomor HP dan Email #

Kita rangkai seluruh atom menjadi pola yang bekerja pada teks Indonesia. Pola email berikut adalah versi sederhana yang cukup untuk teks bersih — sekadar pembuka:

python
import re

kontak = "[email protected] dan [email protected]"
print(re.findall(r"\w+@\w+\.com", kontak))

Output yang Dihasilkan:

Sekarang tugas validasi: sebuah teks diterima hanya bila seluruhnya adalah nomor HP berformat 08xx-xxxx-xxxx.

python
import re

def valid_nomor_hp(nomor):
    pola = r"^08\d\d-\d\d\d\d-\d\d\d\d$"
    return bool(re.search(pola, nomor))

for kandidat in ["0812-3456-7890", "telepon 0812-3456-7890", "0812-3456-78900"]:
    print(kandidat, "->", valid_nomor_hp(kandidat))

Output yang Dihasilkan:

text
0812-3456-7890 -> True
telepon 0812-3456-7890 -> False
0812-3456-78900 -> False

Mari kita telaah apa yang terjadi pada baris-baris kode di atas:

  1. Baris 3 (def valid_nomor_hp(nomor):): fungsi pembungkus menyembunyikan detail pola sehingga pemanggil cukup memanggil satu nama.
  2. Baris 4 (pola = r"^08\d\d-\d\d\d\d-\d\d\d\d$"): anchor ^ dan $ memaksa pola menghabiskan seluruh string, sementara setiap \d mencocokkan tepat satu digit pada posisinya.
  3. Baris 5 (return bool(re.search(pola, nomor))): re.search() mengembalikan object re.Match bila cocok atau None bila gagal; bool() mengubah keduanya menjadi True atau False.
  4. Baris 7-8 (for kandidat in): tiga kandidat diuji — format yang benar diterima; teks berawalan kata telepon ditolak oleh anchor ^; nomor dengan satu digit lebih ditolak oleh anchor $.

Pola r"^08\d\d-\d\d\d\d-\d\d\d\d$" bekerja, tetapi penulisan \d berulang semacam ini melelahkan dan rawan salah hitung. Chapter Character Classes dan Quantifiers memperkenalkan cara meringkas pengulangan tersebut menjadi hitungan yang eksplisit.