Contoh penggunaan regular expression dengan bahasa Singkong


Mari kita andaikan bahwa kita perlu memeriksa apakah sebuah STRING sesuai dengan pola tertentu. Sebagai contoh, diawali dengan huruf A. Di Singkong, kita bisa saja menggunakan fungsi startswith seperti contoh berikut (diketikkan pada Interactive evaluator Singkong):

>  startswith("Hello", "A")
false

>  startswith("A1 B2 C3 D4", "A")
true

Atau, menggunakan contoh STRING sebelumnya, apakah diakhiri dengan angka 4, menggunakan fungsi endswith:

>  endswith("Hello", "4")
false

>  endswith("A1 B2 C3 D4", "4")
true

Tentu saja, kita bisa kombinasikan dengan operator & seperti contoh berikut:

>  var s = "A1 B2 C3 D4"
>  startswith(s, "A") & endswith(s, "4")
true
>  startswith(s, "A") & endswith(s, "5")
false

Bisa kita lihat, kodenya relatif mudah terbaca, namun bisa semakin panjang apabila kita memiliki kriteria yang lebih rumit. Bahkan, sekedar mencocokkan huruf besar atau kecil, kita perlu gunakan fungsi upper atau lower.

Mari kita lihat alternatifnya, menggunakan regular expression (secara sederhana, sebuah STRING dengan aturan tertentu, yang mendefinisikan pola tertentu, misal untuk pencarian dalam STRING). Kita akan mengunakan fungsi matches, dengan ^ sebagai penanda awal baris, dan $ sebagai penanda akhir baris. Contoh berikut, walau kelihatannya sudah sesuai, misal ^A atau 4$, tidaklah memberikan keluaran yang kita harapkan, yang mana harusnya (rasanya) benar bahwa diawali A atau diakhiri 4:

>  matches("A1 B2 C3 D4", "^A")
false

>  matches("A1 B2 C3 D4", "4$")
false

Hal ini karena, fungsi matches mengharapkan keseluruhan STRING. Jadi, apabila ingin ^A dan 4$, apakah kita bisa menggunakan cara berikut?:

>  matches("A1 B2 C3 D4", "^A4$")
false

Sayangnya, tidak sesuai apa yang kita rasa mungkin sudah benar. Yang mana, berlaku untuk contoh berikut:

>  matches("A4", "^A4$")
true

Kita perlu memberi tahu bahwa diantara A dan 4, terdapat sejumlah karakter lain. Perhatikanlah bahwa kita menggunakan karakter titik . untuk karakter apa saja (secara sederhananya), dengan greedy quantifier *, yang artinya nol atau lebih. Sekarang, contoh berikut sesuai apa yang kita inginkan:

>  matches("A1 B2 C3 D4", "^A.*4$")
true

Sudah selesai? Tentu saja belum. Bagaimana kalau kita tidak ingin membedakan huruf besar atau kecil? Kita bisa menambahkan (?i) seperti contoh berikut.

>  matches("a1 b2 c3 d4", "^A.*4$")
false

>  matches("a1 b2 c3 d4", "(?i)^A.*4$")
true

>  matches("A1 B2 C3 D4", "(?i)^A.*4$")
true

Penulis termasuk yang kerap kali tidak ingat atau sulit memahami regular expression. Oleh karena itu, untuk kebutuhan mencocokkan STRING dengan aturan tertentu, kadang penulis menggunakan fungsi-fungsi built-in yang disediakan oleh Singkong, seperti startswith atau endswith, yang dibahas sebelumnya.

Namun, ada kalanya, dengan mengunakan regular expression, kode program menjadi jauh lebih ringkas. Sebagai contoh, ketika kita perlu mengekstrak pola tertentu dari sebuah STRING. Misal, mengekstrak huruf atau angka saja dari STRING berikut. Cukup dengan fungsi extract dan regular expression. Jauh lebih singkat dibandingkan harus membuat parser sendiri atau menggunakan fungsi split (yang mungkin, harus berkali-kali dilakukan).

>  extract("A1 B2 C3 D4", "(\d+)")
["1", "2", "3", "4"]

>  extract("A1 B2 C3 D4", "(\D+)")
["A", " B", " C", " D"]

Dalam contoh tersebut, kita membuat group (dengan kurung), dengan kelas karakter yang telah terdefinisi (\d adalah digit 0 sampai 9, \D adalah non digit). Contoh berikut mensimulasikan versi sederhana dari ekstrak tag, mirip seperti di HTML.

>  extract("Code: <tag>Singkong</tag> Code: <tag>Programming</tag>", "<(\w+)>(.*?)</(\1)>")
["<tag>Singkong</tag>", "<tag>Programming</tag>"]

Dalam hal ini, \w artinya word (a-zA-Z_0-9) dan \1 adalah back reference ke group pertama. Sebaris perintah tersebut, walau kelihatannya menggunakan regular expression yang butuh waktu untuk dipahami lebih lanjut, jauh lebih ringkas dibandingkan dengan menulis parser sendiri.

Kita mungkin bertanya. Bagaimana kalau kita hanya ingin mengambil nilai diantara tag buka dan tag tutup, seperti Singkong dan Programming pada contoh sebelumnya? Untuk kebutuhan tersebut, kita dapat menggunakan fungsi extract_group. Perhatikanlah contoh berikut:

>  extract_group("Code: <tag>Singkong</tag> Code: <tag>Programming</tag>", "<(\w+)>(.*?)</(\1)>")
[["<tag>Singkong</tag>", "tag", "Singkong"], ["<tag>Programming</tag>", "tag", "Programming"]]

Untuk setiap yang bisa kita ekstrak, sebuah ARRAY akan dihasilkan, yang mana elemen di dalam ARRAY tersebut adalah group-group yang didapatkan.

Terima kasih telah membaca :)