Keluarga find yang kita pelajari pada Chapter std::string dan string_view menemukan teks yang ditulis persis; sementara pertanyaan seperti “apakah ada angka tiga digit di sini” atau “ganti semua tag HTML dengan spasi” membutuhkan pola yang bisa menyebut bentuk, bukan isi. Jawabannya regular expression (regex): bahasa kecil untuk mendeskripsikan pola teks. Header <regex> (sejak C++11) menyediakan kelas polanya (std::regex), hasil kecocokan (std::smatch), dan tiga fungsi pencocokan. Chapter ini membahas tiga fungsi utama itu, pengiterasian semua kecocokan dengan regex_iterator, pilihan grammar lewat syntax option, peringatan performa beserta kasus yang lebih cocok diurai manual, dan tips menulis pola yang di-escape dengan benar.
Tiga Fungsi Utama: regex_match, regex_search, dan regex_replace #
Ketiga fungsi menerima pola dan string target, tetapi menjawab pertanyaan yang berbeda. regex_match hanya berhasil bila pola mencakup seluruh string — cocok untuk validasi format. regex_search berhasil bila sebagian string cocok — cocok untuk menemukan dan membaca potongan pertama. regex_replace mengembalikan string baru berisi seluruh string dengan semua kecocokan diganti teks pengganti. Informasi kecocokan dikembalikan lewat objek std::smatch (alias std::match_results untuk std::string): str() memberi teks yang cocok, position() memberi indeks nol-tempat kemunculannya.
| Fungsi | Pertanyaan yang dijawab | Hasil |
|---|---|---|
regex_match | apakah SELURUH string sesuai pola? | bool (versi dengan smatch tersedia) |
regex_search | adakah BAGIAN string yang sesuai pola? | bool (versi dengan smatch tersedia) |
regex_replace | bagaimana string baru setelah penggantian? | std::string |
#include <iostream> // std::cout
#include <regex> // std::regex, regex_match, regex_search, regex_replace
#include <string> // std::string
int main() {
std::cout << std::boolalpha;
std::string teks = "Stok berisi 42 unit, dipesan 7 kali";
std::regex polaAngka{"\\d+"}; // pola dikompilasi SEKALI di awal
std::smatch hasil;
// 1) regex_match: pola harus mencakup SELURUH string
std::regex polaKode{"[A-Z]{3}-\\d{4}"};
std::cout << "match CMD-2024 : " << std::regex_match("CMD-2024", polaKode) << "\n";
std::cout << "match CMD-20240: " << std::regex_match("CMD-20240", polaKode) << "\n";
// 2) regex_search: cukup SATU bagian string yang cocok
if (std::regex_search(teks, hasil, polaAngka)) {
std::cout << "angka pertama : " << hasil.str() << "\n";
std::cout << "di posisi : " << hasil.position() << "\n";
}
// 3) regex_replace: SEMUA kecocokan diganti teks pengganti
std::cout << "setelah replace: " << std::regex_replace(teks, polaAngka, "N") << "\n";
return 0;
}Output yang Dihasilkan:
match CMD-2024 : true
match CMD-20240: false
angka pertama : 42
di posisi : 12
setelah replace: Stok berisi N unit, dipesan N kaliMari kita bedah:
- Baris 9 (
std::regex polaAngka{"\\d+"};): pola dikompilasi sekali di awal. Di string literal C++,\\dmenjadi\ddi dalam pola — dua karakter itu berarti “satu digit” (detail escape dibahas di bagian akhir chapter). - Baris 13-15:
regex_match("CMD-2024", polaKode)menghasilkantruekarena seluruh string pas dengan[A-Z]{3}-\d{4};CMD-20240gagal karena ada satu karakter ekstra yang tidak tercakup pola. - Baris 18-21:
regex_searchmenemukan kecocokan pertama dan mengisinya kehasil;hasil.str()tercetak42danhasil.position()tercetak12— indeks karakter pertama dari42di dalam string target. - Baris 24:
regex_replacetidak mengubahteks; ia mengembalikan string baru dengan kedua angka (42dan7) digantiN.
Mengiterasi Semua Kecocokan dengan regex_iterator #
regex_search berhenti pada kecocokan pertama; untuk mendapatkan semua kecocokan, standar menyediakan std::regex_iterator (untuk std::string: alias std::sregex_iterator). Iterator ini dibangun dari pasangan iterator string plus pola, lalu setiap ++ melangkah ke kecocokan berikutnya; *it memberikan objek smatch yang berisi teks dan posisinya. Iterator akhir ditandai oleh sregex_iterator yang dibangun tanpa argumen — idiom yang sama seperti end() pada container. Bila kebutuhan kita hanya mengambil potongan tertentu dari tiap kecocokan (atau potongan antara kecocokan), std::regex_token_iterator adalah varian yang menyaring sub-ekspresi; untuk chapter ini regex_iterator saja sudah cukup.
#include <iostream> // std::cout
#include <regex> // std::regex, std::sregex_iterator, std::smatch
#include <string> // std::string
int main() {
std::string log = "hari1: 120 langkah, hari2: 85 langkah, hari3: 232 langkah";
std::regex polaAngka{"\\d+"};
// Iterator akhir: sregex_iterator yang dibangun tanpa argumen
std::sregex_iterator akhir;
int jumlah = 0;
for (std::sregex_iterator it{log.begin(), log.end(), polaAngka};
it != akhir; ++it) {
std::smatch cocok = *it; // satu kecocokan lengkap dengan posisinya
++jumlah;
std::cout << "posisi " << cocok.position()
<< " : " << cocok.str() << "\n";
}
std::cout << "jumlah kecocokan: " << jumlah << "\n";
return 0;
}Output yang Dihasilkan:
posisi 4 : 1
posisi 7 : 120
posisi 24 : 2
posisi 27 : 85
posisi 43 : 3
posisi 46 : 232
jumlah kecocokan: 6Mari kita bedah:
- Baris 10 (
std::sregex_iterator akhir;): iterator akhir tanpa isi; perbandinganit != akhirmenjadi syarat berhenti loop. - Baris 13-14: iterator awal dibangun dari
log.begin(),log.end(), dan pola; konstruktor itu langsung mencari kecocokan pertama, dan loop berjalan selama masih ada. - Baris 15:
*itmengembalikansmatch— bukan cuma teks, tetapi juga posisi, prefix, dan suffix relatif terhadap string target. - Baris 17-18: tiap kecocokan dicetak bersama
position()-nya; keenam angka pada log (“1”, “120”, “2”, “85”, “3”, “232”) terangkat lengkap dengan indeksnya. - Baris 20: penghitung sederhana menjawab “ada berapa kecocokan” — enam angka.
Syntax Option: Grammar ECMAScript dan Variasinya #
Konstruktor std::regex menerima argumen kedua bertipe syntax_option_type — bitmask di namespace std::regex_constants (nilai yang sama juga tersedia sebagai anggota std::regex). Opsi terbagi dua kelompok. Kelompok grammar memilih bahasa polanya: ECMAScript, basic, extended, awk, grep, egrep; dari keenamnya paling banyak satu saja boleh dipilih, dan bila tidak ada yang disebut, ECMAScript dipakai sebagai bawaan. Kelompok variasi melengkapi grammar mana pun: icase (mengabaikan huruf besar/kecil), nosubs (grup tidak disimpan), optimize (pencocokan lebih cepat, konstruksi lebih lambat), collate (rentang karakter seperti [a-b] peka locale), dan multiline (C++17: ^/$ mencocokkan tiap baris). Salah satu fitur khas grammar ECMAScript adalah kuantor non-greedy: *? dan +? berhenti pada peluang pertama, sedangkan bentuk serakahnya (*, +) menelan sebanyak mungkin.
#include <iostream> // std::cout
#include <regex> // std::regex, std::regex_constants
#include <string> // std::string
int main() {
std::cout << std::boolalpha;
// icase: pencocokan mengabaikan huruf besar/kecil
std::regex pola{"Gagal", std::regex_constants::icase};
std::cout << "search GAGAL : "
<< std::regex_search("proses GAGAL total", pola) << "\n";
// Grammar bawaan ECMAScript mendukung kuantor non-greedy (?)
std::string teks = "<b>tebal</b><i>miring</i>";
std::smatch m;
std::regex serakah{"(<.+>)"}; // .+ : serakah, telan sebanyak mungkin
std::regex malas{"(<.+?>)"}; // .+? : malas, berhenti di peluang pertama
std::regex_search(teks, m, serakah);
std::cout << "greedy : " << m[1] << "\n";
std::regex_search(teks, m, malas);
std::cout << "lazy : " << m[1] << "\n";
return 0;
}Output yang Dihasilkan:
search GAGAL : true
greedy : <b>tebal</b><i>miring</i>
lazy : <b>Mari kita bedah:
- Baris 9 (
std::regex pola{"Gagal", std::regex_constants::icase};):icaseditambahkan di atas grammar bawaan; bentukstd::regex::icasesetara karena nilainya diduplikasi distd::regex. - Baris 10-11: pola
"Gagal"denganicasemenemukanGAGALdi dalam string target — pencocokan mengabaikan perbedaan huruf besar/kecil. - Baris 17-18: dua pola berbeda hanya pada kuantornya:
.+serakah versus.+?malas (fitur ECMAScript). - Baris 19-22: versi serakah menelan dari
<pertama sampai>terakhir sehingga satu grup menampung seluruh baris; versi malas berhenti di>pertama dan menangkap<b>saja.
Kapan Parsing Manual Lebih Tepat #
std::regex nyaman, tetapi bukan alat gratis. Konstruksi std::regex menguraikan dan menyusun pola menjadi mesin pencocokan — pekerjaan yang relatif mahal — sementara pencocokannya sendiri dijalankan mesin serbaguna yang jauh lebih lambat daripada kode yang menyerang format itu secara langsung. Konsekuensinya dua aturan praktis: bangun objek std::regex sekali di luar loop (bukan di dalam setiap panggilan fungsi), dan pertimbangkan parsing manual untuk pekerjaan sederhana yang berulang banyak kali — memecah baris pada satu karakter pemisah, memeriksa awalan atau akhiran, atau menguraikan format tetap. Kemampuan yang dibutuhkan — find, substr, dan npos — sudah kita kuasai pada Chapter std::string dan string_view.
#include <iostream> // std::cout
#include <string> // std::string, find, substr, npos
#include <vector> // std::vector
// Memecah baris CSV sederhana TANPA regex: find + substr saja
std::vector<std::string> pecah(const std::string& baris, char pemisah) {
std::vector<std::string> hasil;
std::size_t mulai = 0;
while (true) {
std::size_t koma = baris.find(pemisah, mulai);
// npos berarti tidak ada lagi pemisah: substr sampai akhir baris
hasil.push_back(baris.substr(mulai, koma - mulai));
if (koma == std::string::npos) break;
mulai = koma + 1;
}
return hasil;
}
int main() {
std::string baris = "Budi,Siti,Dewi";
for (const std::string& nama : pecah(baris, ',')) {
std::cout << nama << "\n";
}
return 0;
}Output yang Dihasilkan:
Budi
Siti
DewiMari kita bedah:
- Baris 6-8 (
pecah): fungsi menerima satu baris dan satu karakter pemisah; hasilnyastd::vector<std::string>— tanpa regex dan tanpa alokasi pola. - Baris 10-12:
findmencari pemisah berikutnya dari posisimulai;substr(mulai, koma - mulai)memotong potongan sebelum pemisah itu. - Baris 13-14: bila
findmengembalikannpos(pemisah tidak ada lagi), potongan terakhir diambil lalu loop berhenti; selain itu pencarian dilanjutkan setelah pemisah. - Baris 20-22: pemanggil menguraikan
"Budi,Siti,Dewi"menjadi tiga nama — tugas yang jika ditulis regex akan membayar biaya mesin pola untuk pola yang sebenarnya sudah pasti bentuknya.
Tips Escape String #
Dua lapis bahasa bertemu di sini: string literal C++ dan bahasa regex. Karakter yang istimewa di regex — . ^ $ | ( ) [ ] { } * + ? \ — harus diawali \ agar dibaca sebagai karakter biasa; dan karena \ juga istimewa di string literal C++, kita menulisnya ganda: "\\." di kode menjadi \. di pola. Kebalikannya juga perlu diwaspadai: menulis "\d" (satu backslash) membuat compiler membaca \d sebagai escape string yang tidak dikenal — GCC melaporkannya sebagai warning unknown escape sequence, dan hasilnya bukan pola yang kita maksud. Kebiasaan yang sehat: pola selalu ditulis dengan backslash ganda untuk kelas karakter (\\d, \\w, \\s) dan tanda escape-nya. Bila pola datang dari input pengguna saat runtime (misalnya kata yang dicari), tidak ada fungsi standar yang meng-escape-nya — kita perlu menambahkan \ sebelum tiap karakter khusus secara manual, atau membatasi karakter yang diterima.
#include <iostream> // std::cout
#include <regex> // std::regex, regex_search
#include <string> // std::string
int main() {
std::cout << std::boolalpha;
// Tanpa escape: titik (.) cocok dengan karakter APA PUN
std::regex longgar{"foto.png"};
std::cout << "fotoXpng vs longgar: "
<< std::regex_search("fotoXpng", longgar) << "\n";
// Dengan escape: pola \\. hanya cocok dengan titik sungguhan
std::regex ketat{"foto\\.png"};
std::cout << "foto.png vs ketat : "
<< std::regex_search("foto.png", ketat) << "\n";
std::cout << "fotoXpng vs ketat : "
<< std::regex_search("fotoXpng", ketat) << "\n";
// Pola nama file gambar: kelas karakter, kuantor, dan anchor $
std::regex gambar{"\\w+\\.(png|jpg)$"};
std::cout << "libur.jpg vs gambar: "
<< std::regex_search("libur.jpg", gambar) << "\n";
std::cout << "lapor.txt vs gambar: "
<< std::regex_search("lapor.txt", gambar) << "\n";
return 0;
}Output yang Dihasilkan:
fotoXpng vs longgar: true
foto.png vs ketat : true
fotoXpng vs ketat : false
libur.jpg vs gambar: true
lapor.txt vs gambar: falseMari kita bedah:
- Baris 9 (
std::regex longgar{"foto.png"};): titik tanpa escape berarti “karakter apa pun”, sehinggafotoXpngpun cocok — sering bukan niat kita. - Baris 13-14: pola
"foto\\.png"berisi\.yang membaca titik secara harfiah;foto.pngcocok danfotoXpngditolak. - Baris 21 (
"\\w+\\.(png|jpg)$"): gabungan kelas karakter\w+, titik harfiah, grup alternatif(png|jpg), dan anchor$yang menuntut kecocokan di ujung string. - Baris 22-25:
libur.jpglolos sedangkanlapor.txtditolak — ekstensi lain tidak tercakup pola.
Pola teks kini tercakup: dari kecocokan tunggal sampai penggantian massal. Chapter berikutnya membahas std::bitset, himpunan bit berukuran tetap yang menjadi rumah flag dan operasi bitwise pada utilitas standar.
Rujukan resmi: cppreference — Regular expressions library , cppreference — regex_iterator , cppreference — syntax_option_type .