Mencari hanyalah setengah pekerjaan; sehari-hari kita lebih sering perlu mengganti: menyeragamkan format tanggal, menyensor angka sensitif, atau merapikan teks masukan pengguna. Fungsi re.sub() menjawab kebutuhan tersebut untuk teks berpola — teks yang isinya berubah-ubah namun strukturnya bisa dideskripsikan. Chapter ini membahas re.sub() mulai dari penggantian sederhana, backreference, replacement function, sampai studi kasus menyamarkan log.

re.sub dan Backreference #

Bentuk dasarnya re.sub(pola, pengganti, teks): setiap kecocokan pola diganti dengan string pengganti.

python
import re

teks = "Harga total 150000 rupiah, diskon 20000 rupiah."
print(re.sub(r"rupiah", "IDR", teks))

Output yang Dihasilkan:

text
Harga total 150000 IDR, diskon 20000 IDR.

Untuk teks tetap seperti contoh di atas, method str.replace() dari Chapter Searching dan Replacing Text sebenarnya lebih tepat dan lebih cepat — re.sub() baru menunjukkan keunggulannya saat yang dicari berpola. Kekuatan pertamanya adalah backreference: dalam string pengganti, \1, \2, dan seterusnya merujuk isi capture group dari kecocokan, sehingga potongan bisa dipindah susunannya:

python
import re

catatan = "Rapat dipindah dari 2025-06-15 ke 2025-07-01."
print(re.sub(r"(\d{4})-(\d{2})-(\d{2})", r"\3/\2/\1", catatan))

Output yang Dihasilkan:

text
Rapat dipindah dari 15/06/2025 ke 01/07/2025.

Tiga group menangkap tahun, bulan, dan tanggal; string pengganti r"\3/\2/\1" menuliskannya kembali dengan urutan hari-bulan-tahun. Group bernama dari Chapter Groups dan Captures dipanggil dengan \g<nama> — bentuk yang wajib saat rujukan group berdampingan dengan angka:

python
import re

pola = r"(?P<hari>\d{2})/(?P<bulan>\d{2})/(?P<tahun>\d{4})"
print(re.sub(pola, r"\g<tahun>-\g<bulan>-\g<hari>", "Batas akhir 15/06/2025."))

Output yang Dihasilkan:

text
Batas akhir 2025-06-15.

Replacement Function #

String pengganti tidak harus berupa teks statis; re.sub() juga menerima callable. Interpreter memanggil function tersebut satu kali untuk setiap kecocokan, meneruskan object re.Match, lalu memakai nilai baliknya sebagai string pengganti. Contoh pertama: menyensor setiap deretan digit menjadi deretan bintang:

python
import re

def sensor_digit(cocok):
    return "*" * len(cocok.group())

pesan = "Kartu 4821-9930 tersisa saldo 150000."
print(re.sub(r"\d+", sensor_digit, pesan))

Output yang Dihasilkan:

text
Kartu ****-**** tersisa saldo ******.

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

  1. Baris 3 (def sensor_digit(cocok):): function menerima satu parameter — object re.Match untuk kecocokan yang sedang diganti.
  2. Baris 4 (return "*" * len(cocok.group())): method cocok.group() berisi deretan digit yang tertangkap; len() menghitung panjangnya, sehingga jumlah bintang selalu sama dengan jumlah digit yang disensor.
  3. Baris 7 (re.sub(r"\d+", sensor_digit, pesan)): setiap kecocokan pola \d+ diganti dengan hasil panggilan sensor_digit — penggantian dihitung per kecocokan, bukan satu string untuk semua.

Contoh kedua memakai teknik yang sama untuk merapikan nama produk menjadi kapital di awal:

python
import re

def judulkan(cocok):
    return cocok.group().capitalize()

produk = "kopi tubruk, teh melati, air mineral"
print(re.sub(r"[a-z]+", judulkan, produk))

Output yang Dihasilkan:

text
Kopi Tubruk, Teh Melati, Air Mineral

Menghitung Penggantian dengan subn #

Fungsi re.subn() bekerja seperti re.sub() tetapi mengembalikan tuple: string hasil dan jumlah penggantian yang terjadi. Parameter count membatasi jumlah penggantian bila hanya sebagian yang perlu diganti:

python
import re

laporan = "kopi, teh, kopi, kopi, jus"
teks_baru, jumlah = re.subn(r"kopi", "teh", laporan)
print(teks_baru)
print("jumlah penggantian:", jumlah)
print(re.sub(r"kopi", "jus", laporan, count=1))

Output yang Dihasilkan:

text
teh, teh, teh, teh, jus
jumlah penggantian: 3
jus, teh, kopi, kopi, jus

Studi Kasus: Menyamarkan Log #

Log aplikasi sering berisi alamat IP dan token yang tidak boleh ikut keluar dari ruang server. Dua panggilan re.sub() menyelesaikan penyamarannya:

python
import re

log = (
    "2025-06-15 09:30:01 INFO login user=budi ip=10.1.24.7\n"
    "2025-06-15 09:31:15 WARN login gagal user=siti ip=10.1.24.99\n"
    "2025-06-15 09:32:40 INFO token=YOUR_API_TOKEN dipakai oleh budi"
)

log = re.sub(r"ip=\d+\.\d+\.\d+\.\d+", "ip=[REDACTED]", log)
log = re.sub(r"token=\w+", "token=[REDACTED]", log)
print(log)

Output yang Dihasilkan:

text
2025-06-15 09:30:01 INFO login user=budi ip=[REDACTED]
2025-06-15 09:31:15 WARN login gagal user=siti ip=[REDACTED]
2025-06-15 09:32:40 INFO token=[REDACTED] dipakai oleh budi

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

  1. Baris 3 (log = (): tiga baris log dirangkai menjadi satu string; penanda \n memisahkan baris, dan nilai waktunya statis — bukan jam berjalan — agar contoh bisa diperiksa ulang dengan hasil yang sama.
  2. Baris 9: pola ip=\d+\.\d+\.\d+\.\d+ menangkap tulisan ip= beserta empat segmen angka; setiap titik di-escape menjadi \. agar berarti titik literal sesuai aturan dari Chapter Pola Dasar Regex.
  3. Baris 10: pola token=\w+ menyamarkan seluruh nilai token tanpa perlu mengetahui panjang nilainya.
  4. Baris 11: hasil cetak memperlihatkan kedua jenis data sensitif telah terganti.

Dengan pola, backreference, dan replacement function di tangan, otomasi teks sehari-hari — normalisasi, sensor, redaksi — tinggal soal merangkai ketiganya.