Saran dalam artikel ini terutama berfokus pada keterbacaan ekspresi reguler. Dengan mengembangkan kebiasaan ini selama pengembangan, Anda akan mempertimbangkan desain dan struktur ekspresi dengan lebih jelas, yang akan membantu mengurangi bug dan pemeliharaan kode adalah pengelola kode ini sendiri. Anda dapat melihatnya sendiri dan memperhatikan pengalaman ini dengan ekspresi reguler dalam penggunaan Anda yang sebenarnya.
Ekspresi reguler sulit untuk ditulis, sulit dibaca, dan sulit dipelihara. Ekspresi reguler sering kali tidak cocok dengan teks yang tidak diharapkan atau kehilangan teks yang valid. Kombinasi kemampuan dan nuansa setiap metakarakter membuat kode tidak mungkin diinterpretasikan tanpa menggunakan trik intelektual.
Banyak alat yang menyertakan fitur yang memudahkan membaca dan menulis ekspresi reguler, namun juga sangat non-idiomatis. Bagi banyak programmer, menulis ekspresi reguler adalah seni ajaib. Mereka berpegang pada karakteristik yang mereka ketahui dan memiliki sikap optimisme mutlak. Jika Anda ingin menerapkan lima kebiasaan yang dibahas dalam artikel ini, Anda akan dapat merancang ekspresi reguler yang tahan terhadap trial and error.
Artikel ini akan menggunakan bahasa Perl, PHP, dan Python sebagai contoh kode, namun saran dalam artikel ini berlaku untuk hampir semua implementasi ekspresi pengganti (regex).
1. Gunakan spasi dan komentar.
Bagi sebagian besar programmer, penggunaan spasi dan indentasi dalam lingkungan ekspresi reguler tidak menjadi masalah. Jika mereka tidak melakukan ini, mereka pasti akan ditertawakan oleh rekan-rekan mereka dan bahkan orang awam. Hampir semua orang tahu bahwa memasukkan kode ke dalam satu baris akan menyulitkan pembacaan, penulisan, dan pemeliharaan. Apa perbedaan ekspresi reguler?
Sebagian besar alat ekspresi pengganti memiliki fitur spasi putih yang diperluas, yang memungkinkan pemrogram memperluas ekspresi regulernya menjadi beberapa baris dan menambahkan komentar di akhir setiap baris. Mengapa hanya sedikit programmer yang memanfaatkan fitur ini? Ekspresi reguler Perl 6 menggunakan pola perluasan ruang secara default. Jangan biarkan bahasa memperluas ruang secara default untuk Anda, manfaatkan sendiri.
Salah satu trik yang perlu diingat tentang spasi yang diperluas adalah dengan memberi tahu mesin ekspresi reguler untuk mengabaikan spasi yang diperluas. Dengan cara ini jika Anda perlu mencocokkan spasi, Anda harus menentukannya secara eksplisit.
Dalam bahasa Perl, tambahkan x di akhir ekspresi reguler, sehingga "m/foo bar/" menjadi bentuk berikut:
m/
foo
batang
/x
Dalam bahasa PHP, tambahkan x di akhir ekspresi reguler, sehingga ""/foo bar/"" menjadi bentuk berikut:
"/
foo
batang
/x"
Dalam bahasa Python, lewati parameter modifikasi pola "re.VERBOSE" untuk mendapatkan fungsi yang dikompilasi sebagai berikut:
pattern = r'''
foo
batang
'''
regex = re.compile(pattern, re.VERBOSE)
menangani ekspresi reguler yang lebih kompleks, spasi dan komentar akan menjadi lebih penting. Misalkan ekspresi reguler berikut digunakan untuk mencocokkan nomor telepon di Amerika Serikat:
(?d{3})? ?d{3}[-.]d{4}
Ekspresi reguler ini cocok dengan nomor telepon seperti "( 314)555-4000", menurut Anda apakah ekspresi reguler ini cocok dengan "314-555-4000" atau "555-4000"? Jawabannya adalah tidak ada yang cocok. Penulisan baris kode seperti itu menyembunyikan kekurangan dan hasil desain itu sendiri. Kode area telepon diperlukan, tetapi ekspresi reguler tidak memiliki simbol pemisah antara kode area dan awalan.
Memecah baris kode ini menjadi beberapa baris dan menambahkan komentar akan mengungkap kekurangannya dan membuatnya lebih mudah untuk dimodifikasi.
Dalam bahasa Perl seharusnya dalam bentuk berikut:
/
(? # tanda kurung opsional
d{3} # Kode area telepon yang diperlukan
)? # tanda kurung opsional
[-s.]? # Pembatasnya bisa berupa tanda hubung, spasi, atau titik
d{3} # Awalan tiga digit
[-.] # Pembatas lainnya
d{4} # Nomor telepon empat digit
/x
Regex yang ditulis ulang sekarang memiliki pemisah opsional setelah kode area, sehingga harus cocok dengan "314-555-4000", namun kode area tetap diperlukan. Pemrogram lain yang ingin menjadikan kode area telepon opsional dapat dengan cepat melihat bahwa ini bukan opsional, dan perubahan kecil dapat menyelesaikan masalah.
2.
Ada tiga tingkat pengujian dalam tes menulis. Setiap tingkat menambahkan lapisan keandalan pada kode Anda. Pertama, Anda perlu memikirkan baik-baik kode apa yang perlu Anda cocokkan dan apakah Anda dapat menangani ketidakcocokan. Kedua, Anda perlu menggunakan instance data untuk menguji ekspresi reguler. Terakhir, Anda harus lulus panel tes secara resmi.
Memutuskan apa yang akan dicocokkan sebenarnya adalah tentang menemukan keseimbangan antara mencocokkan hasil yang salah dan melewatkan hasil yang benar. Jika regex Anda terlalu ketat, beberapa kecocokan yang benar akan hilang; jika terlalu longgar, itu akan menghasilkan kecocokan yang salah. Setelah ekspresi reguler dirilis ke dalam kode sebenarnya, Anda mungkin tidak memperhatikan keduanya. Perhatikan contoh nomor telepon di atas, yang cocok dengan "800-555-4000 = -5355". Kecocokan yang salah sebenarnya sulit dideteksi, jadi penting untuk membuat rencana ke depan dan mengujinya dengan baik.
Melanjutkan dengan contoh nomor telepon, jika Anda mengonfirmasi nomor telepon dalam formulir web, Anda mungkin puas dengan sepuluh digit nomor dalam format apa pun. Namun, jika Anda ingin memisahkan nomor telepon dari sejumlah besar teks, Anda mungkin perlu hati-hati mengecualikan kecocokan palsu yang tidak memenuhi persyaratan.
Saat memikirkan data yang ingin Anda cocokkan, tuliskan beberapa skenario kasus. Tulis beberapa kode untuk menguji ekspresi reguler Anda terhadap skenario kasus. Untuk ekspresi reguler kompleks apa pun, yang terbaik adalah menulis program kecil untuk mengujinya, yang dapat mengambil bentuk spesifik berikut.
Dalam bahasa Perl:
#!/usr/bin/Perl
my @tests = ( "314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
);
mencari $test saya (@tests) {
jika ( $uji =~ m/
(? # tanda kurung opsional
d{3} # Kode area telepon yang diperlukan
)? # tanda kurung opsional
[-s.]? # Pembatasnya bisa berupa tanda hubung, spasi, atau titik
d{3} # Awalan tiga digit
[-s.] # Pembatas lainnya
d{4} # Nomor telepon empat digit
/X ) {
print "Cocok pada $testn";
}
kalau tidak {
print "Gagal mencocokkan $testn";
}
}
Dalam bahasa PHP:
<?php
$tes = susunan( "314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345" );
$regex = "/
(? # tanda kurung opsional
d{3} # Kode area telepon yang diperlukan
)? # tanda kurung opsional
[-s.]? # Pembatasnya bisa berupa tanda hubung, spasi, atau titik
d{3} # Awalan tiga digit
[-s.] # Pembatas lainnya
d{4} # Nomor telepon empat digit
/x";
foreach ($tes sebagai $tes) {
if (preg_match($regex, $test)) {
echo "Cocok di $test
;";
}
kalau tidak {
echo "Gagal mencocokkan pada $test
;";
}
}
?>;
Dalam bahasa Python:
import ulang
tes = ["314-555-4000",
"800-555-4400",
"(314)555-4000",
"314.555.4000",
"555-4000",
"aasdklfjklas",
"1234-123-12345"
]
pola = r'''
(? # tanda kurung opsional
d{3} # Kode area telepon yang diperlukan
)? # tanda kurung opsional
[-s.]? # Pembatasnya bisa berupa tanda hubung, spasi, atau titik
d{3} # Awalan tiga digit
[-s.] # Pembatas lainnya
d{4} # Nomor telepon empat digit
'''
regex = re.compile( pattern, re.VERBOSE ) untuk pengujian dalam pengujian:
jika regex.cocok(uji):
cetak "Cocok pada", uji, "n"
kalau tidak:
print "Gagal mencocokkan", uji, "n"
Menjalankan kode pengujian akan menunjukkan masalah lain: cocok dengan "1234-123-12345".
Secara teori, Anda perlu mengintegrasikan semua pengujian untuk keseluruhan aplikasi ke dalam tim penguji. Meskipun Anda belum memiliki grup pengujian, pengujian ekspresi reguler Anda akan menjadi dasar yang baik untuk melakukannya, dan sekarang adalah saat yang tepat untuk memulainya. Meskipun ini bukan waktu yang tepat untuk membuatnya, Anda tetap harus menjalankan dan menguji ekspresi reguler setelah setiap modifikasi. Menghabiskan sedikit waktu di sini akan menyelamatkan Anda dari banyak masalah.
3. Operasi bolak-balik grup
Simbol operasi bolak-balik ( ) memiliki prioritas rendah, yang berarti sering kali bergantian lebih dari yang diinginkan pemrogram. Misalnya, ekspresi reguler untuk mengekstrak alamat email dari teks mungkin seperti berikut:
^CC: To:(.*)
Upaya di atas salah, namun bug ini sering kali tidak diperhatikan. Tujuan dari kode di atas adalah untuk menemukan teks yang dimulai dengan "CC:" atau "To:" dan kemudian mengekstrak alamat email di akhir baris ini.
Sayangnya, jika "Kepada:" muncul di tengah baris, ekspresi reguler ini tidak akan menangkap baris apa pun yang dimulai dengan "CC:" dan malah akan mengekstrak beberapa potongan teks acak. Sejujurnya, ekspresi reguler cocok dengan baris yang dimulai dengan "CC:" tetapi tidak menangkap apa pun; atau cocok dengan baris mana pun yang berisi "Kepada:" tetapi menangkap sisa baris. Biasanya, ekspresi reguler ini akan menangkap sejumlah besar alamat email, jadi tidak ada yang akan menyadari bug tersebut.
Jika ingin memenuhi maksud sebenarnya, maka sebaiknya tambahkan tanda kurung agar jelas. Ekspresi regulernya adalah sebagai berikut:
(^CC:) (Kepada:(.*))
Jika maksud sebenarnya adalah menangkap teks yang dimulai dengan " CC:" atau "Kepada:" sisa baris, maka ekspresi reguler yang benar adalah:
^(CC: Ke:)(.*)
Ini adalah bug pencocokan tidak lengkap yang umum yang akan Anda hindari jika Anda terbiasa mengelompokkan untuk operasi bergantian Kesalahan ini.
4. Gunakan bilangan longgar.
Banyak programmer menghindari penggunaan bilangan longgar seperti "*?", "+?" dan "??", meskipun hal tersebut akan membuat ekspresi lebih mudah untuk ditulis dan dipahami.
Pengukur santai mencocokkan teks sesedikit mungkin, yang membantu keberhasilan pencocokan tepat. Jika Anda menulis "foo(.*?)bar", pembilang akan berhenti cocok saat pertama kali menemukan "bar", bukan terakhir kali. Ini penting jika Anda ingin menangkap "###" dari "foo###bar+++bar". Penghitung yang ketat akan menangkap "###bar++ +". ;), ini akan menimbulkan banyak masalah. Jika Anda menggunakan bilangan santai, Anda dapat menghasilkan ekspresi reguler baru dengan menghabiskan sedikit waktu untuk menyusun tipe karakter.
Pengukur santai sangat berguna bila Anda mengetahui struktur konteks di mana Anda ingin menangkap teks.
5. Gunakan pembatas yang tersedia.
Bahasa Perl dan PHP sering kali menggunakan garis miring kiri (/) untuk menandai awal dan akhir ekspresi reguler. Jika Anda bersikeras menggunakan garis miring kiri di Perl dan PHP, Anda sebaiknya menghindari garis miring apa pun dalam ekspresi; jika Anda menggunakan tanda kutip di Python, Anda sebaiknya menghindari garis miring terbalik (). Memilih pembatas atau tanda kutip yang berbeda dapat memungkinkan Anda menghindari setengah dari ekspresi reguler. Hal ini akan membuat ekspresi lebih mudah dibaca dan mengurangi potensi bug yang disebabkan oleh lupa menghindari simbol.
Bahasa Perl dan PHP mengizinkan karakter non-numerik dan spasi apa pun untuk digunakan sebagai pembatas. Jika Anda beralih ke pembatas baru, Anda dapat menghindari hilangnya garis miring kiri saat mencocokkan URL atau tag HTML (seperti "http://" atau "<br/>;").
Misalnya, "/http://(S)*/" dapat ditulis sebagai "#http://(S)*#".
Pembatas yang umum adalah "#", "!" dan " ". Jika Anda menggunakan tanda kurung siku, tanda kurung siku, atau tanda kurung kurawal, usahakan tetap cocok. Berikut adalah beberapa contoh pembatas yang umum:
#…# !…! {…} s … … (khusus Perl) s[…][…] (khusus Perl) s<…>;/…/ (khusus Perl)
Di Python, ekspresi reguler pertama-tama diperlakukan sebagai string. Jika Anda menggunakan tanda kutip sebagai pembatas, Anda akan kehilangan semua garis miring terbalik. Namun Anda dapat menghindari masalah ini dengan menggunakan string "r''". Jika Anda menggunakan tiga tanda kutip tunggal berturut-turut untuk opsi "re.VERBOSE", Anda dapat memasukkan baris baru. Misalnya, regex = "( file://w+)(//d +)" dapat ditulis dalam bentuk berikut:
regex = r'''
(w+)
(d+)
'''