Mencari satu kata di dalam teks mudah — contains atau indexOf menyelesaikannya. Namun bagaimana bila yang dicari bukan teks tetap melainkan pola: “semua deretan angka”, “teks yang diapit tanda kurung sudut”, atau “alamat surel yang bentuknya masuk akal”? Untuk itulah hadir regular expression (regex): bahasa mini untuk mendeskripsikan pola teks, yang di Java dijalankan lewat class java.util.regex.Pattern dan java.util.regex.Matcher. Chapter ini membahas pola dasar, penggunaan Pattern yang dikompilasi sekali lalu dipakai ulang, method find dan group, backreference $1 pada replaceAll, serta perbedaan quantifier greedy dan lazy.
Pattern, Matcher, find dan group #
Dua class bekerja berpasangan. Pattern.compile("\\d+") mengompilasi teks pola menjadi struktur pencarian yang siap pakai — \d berarti satu digit, + berarti “satu atau lebih”, sehingga pola tersebut mendeskripsikan deretan angka. Method matcher(teks) kemudian membuat objek Matcher yang menelusuri teks tertentu; find() maju dari satu kecocokan ke kecocokan berikutnya, dan group() membaca teks yang berhasil dicocokkan.
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PolaRegex {
public static void main(String[] args) {
Pattern pola = Pattern.compile("\\d+");
Matcher pencari = pola.matcher("Pesan dari 0812 selesai pada jam 17");
while (pencari.find()) {
System.out.println("Ketemu \"" + pencari.group() + "\" mulai indeks " + pencari.start());
}
String tukar = "Budi Santoso".replaceAll("(\\w+) (\\w+)", "$2 $1");
System.out.println("Tukar kata: " + tukar);
String bersih = "Budi Siti Dewi".replaceAll("\\s+", " ");
System.out.println("Rapikan spasi: " + bersih);
}
}Output yang Dihasilkan:
Ketemu "0812" mulai indeks 11
Ketemu "17" mulai indeks 33
Tukar kata: Santoso Budi
Rapikan spasi: Budi Siti DewiMari kita telaah baris-baris kode di atas:
- Baris 6 (
Pattern.compile("\\d+")): kompilasi pola sekali di awal. Dalam kode nyata, pola seperti ini sebaiknya disimpan distatic final Patterndan dipakai ulang — kompilasi regex berbiaya, sedangkan satu objekPatternaman dipakai berkali-kali dan oleh banyak thread. - Baris 7 (
pola.matcher(...)): membuatMatcheruntuk teks tertentu. SatuMatcherhanya untuk satu teks dan tidak untuk dipakai bersama antar thread; buat baru lewatpola.matcher(teksLain)bila teksnya berganti. - Baris 9-11 (
while (pencari.find())): setiapfind()melompat ke kecocokan berikutnya dan mengembalikantrueselama masih ada. Di dalam loop,group()membaca teks kecocokan ("0812"lalu"17") danstart()posisi awalnya (indeks11dan33). - Baris 13 (
replaceAll("(\\w+) (\\w+)", "$2 $1")):\\w+mencocokkan satu “kata” (huruf/angka/garis bawah). Tanda kurung membentuk capturing group, dan pada teks pengganti,$1dan$2merujuk kelompok pertama dan kedua — efeknya dua kata bertukar posisi menjadiSantoso Budi. - Baris 15 (
replaceAll("\\s+", " ")):\\s+mencocokkan satu atau lebih karakter spasi apa pun (spasi, tab); menggantinya dengan satu spasi merapikan jarak kata yang tidak beraturan.
Kelas Karakter, Anchor, dan Quantifier #
Sekejap kita rangkum kosakata regex yang paling penting: kelas karakter [abc] mencocokkan satu karakter dari pilihan tersebut; anchor ^ dan $ menambat pola ke awal dan akhir teks; quantifier *, +, dan ? menyatakan pengulangan. Satu kehalusan yang kerap mengejutkan: quantifier secara bawaan bersifat greedy — mengambil sebanyak mungkin — dan menjadi lazy bila ditutup tanda tanya, mengambil sesedikit mungkin. Perbedaan tersebut paling mudah dilihat saat mengganti teks berpasangan:
import java.util.regex.Pattern;
public class GreedyLazy {
public static void main(String[] args) {
String teks = "<b>Tebal</b> dan <i>miring</i>";
System.out.println("Greedy: " + teks.replaceAll("<.*>", "#"));
System.out.println("Lazy : " + teks.replaceAll("<.*?>", "#"));
System.out.println("\"abc\".matches(\"[abc]+\") : " + "abc".matches("[abc]+"));
System.out.println("\"abd\".matches(\"[abc]+\") : " + "abd".matches("[abc]+"));
System.out.println("\"Java\".matches(\"^Java$\"): " + "Java".matches("^Java$"));
System.out.println("\"Java 25\".matches(\"^Java$\"): " + "Java 25".matches("^Java$"));
}
}Output yang Dihasilkan:
Greedy: #
Lazy : #Tebal# dan #miring#
"abc".matches("[abc]+") : true
"abd".matches("[abc]+") : false
"Java".matches("^Java$"): true
"Java 25".matches("^Java$"): falseBedah untuk contoh kedua:
- Baris 7 (
<.*>greedy):.*menelan karakter sebanyak mungkin, lalu backtracking menemukan>terakhir — hasilnya satu kecocokan raksasa dari<b>pertama hingga</i>terakhir, dan seluruh teks diganti menjadi#. - Baris 8 (
<.*?>lazy): tanda tanya ekstra membuat.*?berhenti pada>pertama yang ditemui. Kecocokan jatuh pada tiap tag satu per satu —<b>,</b>,<i>,</i>— sehingga hasilnya#Tebal# dan #miring#. - Baris 10-11 (
[abc]+):+mengulang kelas karakter, cocok untuk teks yang seluruh hurufnya berasal dari pilihana,b,c. Teks"abd"gagal karenadtidak termasuk anggota kelas. - Baris 12-13 (
^Java$): tanpa anchor,matchessudah memeriksa seluruh teks; anchor membuat niat tersebut eksplisit."Java 25"gagal karena spasi dan angka berada di luar pola.
Pola Surel Sederhana sebagai Latihan #
Sebagai studi kasus penutup, kita susun pola alamat surel yang sederhana secara sengaja: ^[\w.]+@[\w.]+\.[a-zA-Z]{2,}$ — satu atau lebih karakter kata/titik, lalu @, lalu domain, lalu titik, lalu akhiran huruf minimal dua karakter. Kuantifier {2,} berarti “dua kali atau lebih”.
import java.util.regex.Pattern;
public class ValidasiEmail {
public static void main(String[] args) {
Pattern pola = Pattern.compile("^[\\w.]+@[\\w.]+\\.[a-zA-Z]{2,}$");
String[] kandidat = {
"[email protected]",
"[email protected]",
"budi@contoh",
"budi.example.com",
"budi@@example.com"
};
for (String email : kandidat) {
System.out.println(email + " -> " + pola.matcher(email).matches());
}
}
}Output yang Dihasilkan:
[email protected] -> true
[email protected] -> true
budi@contoh -> false
budi.example.com -> false
budi@@example.com -> falseHasilnya masuk akal: dua alamat pertama lolos; budi@contoh gagal karena tidak ada titik akhiran; budi.example.com gagal karena tanpa @; budi@@example.com gagal karena @ tidak boleh berulang pada bagian tersebut.
Dengan regex di tangan, kita memiliki peralatan lengkap untuk mengenali teks berpola. Chapter terakhir Section ini memindahkan fokus dari pola ke penerapan: studi kasus manipulasi teks sehari-hari — normalisasi nama, format ulang CSV, dan perapian keluaran.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
GreedyLazy.java
Unduh berkasimport java.util.regex.Pattern;
public class GreedyLazy {
public static void main(String[] args) {
String teks = "<b>Tebal</b> dan <i>miring</i>";
System.out.println("Greedy: " + teks.replaceAll("<.*>", "#"));
System.out.println("Lazy : " + teks.replaceAll("<.*?>", "#"));
System.out.println("\"abc\".matches(\"[abc]+\") : " + "abc".matches("[abc]+"));
System.out.println("\"abd\".matches(\"[abc]+\") : " + "abd".matches("[abc]+"));
System.out.println("\"Java\".matches(\"^Java$\"): " + "Java".matches("^Java$"));
System.out.println("\"Java 25\".matches(\"^Java$\"): " + "Java 25".matches("^Java$"));
}
}
PolaRegex.java
Unduh berkasimport java.util.regex.Matcher;
import java.util.regex.Pattern;
public class PolaRegex {
public static void main(String[] args) {
Pattern pola = Pattern.compile("\\d+");
Matcher pencari = pola.matcher("Pesan dari 0812 selesai pada jam 17");
while (pencari.find()) {
System.out.println("Ketemu \"" + pencari.group() + "\" mulai indeks " + pencari.start());
}
String tukar = "Budi Santoso".replaceAll("(\\w+) (\\w+)", "$2 $1");
System.out.println("Tukar kata: " + tukar);
String bersih = "Budi Siti Dewi".replaceAll("\\s+", " ");
System.out.println("Rapikan spasi: " + bersih);
}
}
ValidasiEmail.java
Unduh berkasimport java.util.regex.Pattern;
public class ValidasiEmail {
public static void main(String[] args) {
Pattern pola = Pattern.compile("^[\\w.]+@[\\w.]+\\.[a-zA-Z]{2,}$");
String[] kandidat = {
"[email protected]",
"[email protected]",
"budi@contoh",
"budi.example.com",
"budi@@example.com"
};
for (String email : kandidat) {
System.out.println(email + " -> " + pola.matcher(email).matches());
}
}
}