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.
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:
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 \..
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:
0812-3456-7890 -> False
0812.3456.7890 -> True
0812 3456 7890 -> FalsePrinsip 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:
| Shorthand | Arti | Setara dengan |
|---|---|---|
\d | satu digit | [0-9] |
\D | satu karakter non-digit | [^0-9] |
\w | satu huruf, digit, atau underscore | [a-zA-Z0-9_] |
\W | kebalikan dari \w | [^a-zA-Z0-9_] |
\s | satu whitespace: spasi, tab, baris baru | |
\S | kebalikan 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 -.
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:
['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.
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:
['PENTING']
['PENTING']
FalseKata 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:
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.
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:
0812-3456-7890 -> True
telepon 0812-3456-7890 -> False
0812-3456-78900 -> FalseMari kita telaah apa yang terjadi pada baris-baris kode di atas:
- Baris 3 (
def valid_nomor_hp(nomor):): fungsi pembungkus menyembunyikan detail pola sehingga pemanggil cukup memanggil satu nama. - Baris 4 (
pola = r"^08\d\d-\d\d\d\d-\d\d\d\d$"): anchor^dan$memaksa pola menghabiskan seluruh string, sementara setiap\dmencocokkan tepat satu digit pada posisinya. - Baris 5 (
return bool(re.search(pola, nomor))):re.search()mengembalikan objectre.Matchbila cocok atauNonebila gagal;bool()mengubah keduanya menjadiTrueatauFalse. - Baris 7-8 (
for kandidat in): tiga kandidat diuji — format yang benar diterima; teks berawalan katateleponditolak 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.