Ekspresi Reguler di Kotlin

1. Perkenalan

Kami dapat menemukan penggunaan (atau penyalahgunaan) ekspresi reguler di hampir semua jenis perangkat lunak, dari skrip cepat hingga aplikasi yang sangat kompleks.

Di artikel ini, kita akan melihat cara menggunakan ekspresi reguler di Kotlin.

Kami tidak akan membahas sintaks ekspresi reguler; keakraban dengan ekspresi reguler, secara umum, diperlukan untuk mengikuti artikel secara memadai, dan pengetahuan tentang sintaks Pola Java secara khusus direkomendasikan.

2. Penyiapan

Meskipun ekspresi reguler bukan bagian dari bahasa Kotlin, ekspresi reguler hadir dengan pustaka standarnya.

Kami mungkin sudah memilikinya sebagai ketergantungan proyek kami:

 org.jetbrains.kotlin kotlin-stdlib 1.2.21 

Kami dapat menemukan versi terbaru kotlin-stdlib di Maven Central.

3. Membuat Objek Ekspresi Reguler

Ekspresi reguler adalah contoh kelas kotlin.text.Regex . Kita bisa membuatnya dengan beberapa cara.

Kemungkinannya adalah memanggil konstruktor Regex :

Regex("a[bc]+d?")

atau kita dapat memanggil metode toRegex pada String:

"a[bc]+d?".toRegex()

Akhirnya, kita dapat menggunakan metode pabrik statis:

Regex.fromLiteral("a[bc]+d?")

Simpan dari perbedaan yang dijelaskan di bagian selanjutnya, opsi ini setara dan sesuai dengan preferensi pribadi. Ingatlah untuk konsisten!

Kiat: ekspresi reguler sering kali berisi karakter yang akan ditafsirkan sebagai urutan pelolosan dalam literal String . Dengan demikian, kita dapat menggunakan String mentah untuk melupakan beberapa level pelolosan:

"""a[bc]+d?\W""".toRegex()

3.1. Opsi Pencocokan

Baik konstruktor Regex dan metode toRegex memungkinkan kita untuk menentukan satu opsi tambahan atau satu set:

Regex("a(b|c)+d?", CANON_EQ) Regex("a(b|c)+d?", setOf(DOT_MATCHES_ALL, COMMENTS)) "a(b|c)+d?".toRegex(MULTILINE) "a(b|c)+d?".toRegex(setOf(IGNORE_CASE, COMMENTS, UNIX_LINES))

Opsi disebutkan dalam kelas RegexOption , yang dengan mudah kami impor secara statis pada contoh di atas:

  • IGNORE_CASE - mengaktifkan pencocokan tidak peka huruf besar / kecil
  • MULTILINE - mengubah arti ^ dan $ (lihat Pola)
  • LITERAL - menyebabkan metakarakter atau urutan pelolosan dalam pola tidak diberi arti khusus
  • UNIX_LINES - dalam mode ini, hanya \ n yang dikenali sebagai terminator baris
  • KOMENTAR - mengizinkan spasi dan komentar dalam pola
  • DOT_MATCHES_ALL - menyebabkan titik cocok dengan karakter apa pun, termasuk terminator baris
  • CANON_EQ - memungkinkan kesetaraan dengan dekomposisi kanonik (lihat Pola)

4. Pencocokan

Kami menggunakan ekspresi reguler terutama untuk mencocokkan string input , dan terkadang untuk mengekstrak atau mengganti bagiannya.

Sekarang kita akan melihat secara detail metode yang ditawarkan oleh kelas Regex Kotlin untuk pencocokan Strings.

4.1. Memeriksa Kecocokan Parsial atau Total

Dalam kasus penggunaan ini, kami tertarik untuk mengetahui apakah String atau bagian dari String memenuhi ekspresi reguler kami.

Jika kita hanya membutuhkan kecocokan parsial, kita bisa menggunakan containsMatchIn :

val regex = """a([bc]+)d?""".toRegex() assertTrue(regex.containsMatchIn("xabcdy"))

Jika kita ingin agar seluruh String cocok, kita menggunakan kecocokan :

assertTrue(regex.matches("abcd"))

Perhatikan bahwa kami juga dapat menggunakan pertandingan sebagai operator infix:

assertFalse(regex matches "xabcdy")

4.2. Mengekstrak Komponen Pencocokan

Dalam kasus penggunaan ini, kami ingin mencocokkan String dengan ekspresi reguler dan mengekstrak bagian String.

Kami mungkin ingin mencocokkan seluruh String:

val matchResult = regex.matchEntire("abbccbbd")

Atau kami mungkin ingin mencari substring pertama yang cocok:

val matchResult = regex.find("abcbabbd")

Atau mungkin untuk menemukan semua substring yang cocok sekaligus, sebagai satu Set :

val matchResults = regex.findAll("abcb abbd")

Dalam kedua kasus, jika pertandingan berhasil, hasilnya akan menjadi satu atau lebih contoh kelas MatchResult . Di bagian selanjutnya, kita akan melihat cara menggunakannya.

If the match is not successful, instead, these methods return null or the empty Set in case of findAll.

4.3. The MatchResult Class

Instances of the MatchResult class represent successful matches of some input string against a regular expression; either complete or partial matches (see the previous section).

As such, they have a value, which is the matched String or substring:

val regex = """a([bc]+)d?""".toRegex() val matchResult = regex.find("abcb abbd") assertEquals("abcb", matchResult.value)

And they have a range of indices to indicate what portion of the input was matched:

assertEquals(IntRange(0, 3), matchResult.range)

4.4. Groups and Destructuring

We can also extract groups (matched substrings) from MatchResult instances.

We can obtain them as Strings:

assertEquals(listOf("abcb", "bcb"), matchResult.groupValues)

Or we can also view them as MatchGroup objects consisting of a value and a range:

assertEquals(IntRange(1, 3), matchResult.groups[1].range)

The group with index 0 is always the entire matched String. Indices greater than 0, instead, represent groups in the regular expression, delimited by parentheses, such as ([bc]+) in our example.

We can also destructure MatchResult instances in an assignment statement:

val regex = """([\w\s]+) is (\d+) years old""".toRegex() val matchResult = regex.find("Mickey Mouse is 95 years old") val (name, age) = matchResult!!.destructured assertEquals("Mickey Mouse", name) assertEquals("95", age)

4.5. Multiple Matches

MatchResult also has a next method that we can use to obtain the next match of the input String against the regular expression, if there is any:

val regex = """a([bc]+)d?""".toRegex() var matchResult = regex.find("abcb abbd") assertEquals("abcb", matchResult!!.value) matchResult = matchResult.next() assertEquals("abbd", matchResult!!.value) matchResult = matchResult.next() assertNull(matchResult)

As we can see, next returns null when there are no more matches.

5. Replacing

Another common use of regular expressions is replacing matching substrings with other Strings.

For this purpose, we have two methods readily available in the standard library.

One, replace, is for replacing all occurrences of a matching String:

val regex = """(red|green|blue)""".toRegex() val beautiful = "Roses are red, Violets are blue" val grim = regex.replace(beautiful, "dark") assertEquals("Roses are dark, Violets are dark", grim)

The other, replaceFirst, is for replacing only the first occurrence:

val shiny = regex.replaceFirst(beautiful, "rainbow") assertEquals("Roses are rainbow, Violets are blue", shiny)

5.1. Complex Replacements

For more advanced scenarios, when we don't want to replace matches with constant Strings, but we want to apply a transformation instead, Regex still gives us what we need.

Enter the replace overload taking a closure:

val reallyBeautiful = regex.replace(beautiful) { m -> m.value.toUpperCase() + "!" } assertEquals("Roses are RED!, Violets are BLUE!", reallyBeautiful)

As we can see, for each match, we can compute a replacement String using that match.

6. Splitting

Finally, we might want to split a String into a list of substrings according to a regular expression. Again, Kotlin's Regex has got us covered:

val regex = """\W+""".toRegex() val beautiful = "Roses are red, Violets are blue" assertEquals(listOf( "Roses", "are", "red", "Violets", "are", "blue"), regex.split(beautiful))

Here, the regular expression matches one or more non-word characters, so the result of the split operation is a list of words.

We can also put a limit on the length of the resulting list:

assertEquals(listOf("Roses", "are", "red", "Violets are blue"), regex.split(beautiful, 4))

7. Java Interoperability

Jika kita perlu meneruskan ekspresi reguler kita ke kode Java, atau API bahasa JVM lain yang mengharapkan instance java.util.regex.Pattern , kita cukup mengonversi Regex kita :

regex.toPattern()

8. Kesimpulan

Dalam artikel ini, kami telah memeriksa dukungan ekspresi reguler di pustaka standar Kotlin.

Untuk informasi lebih lanjut, lihat referensi Kotlin.

Penerapan semua contoh dan cuplikan kode ini dapat ditemukan di proyek GitHub - ini adalah proyek Maven, jadi semestinya mudah untuk mengimpor dan menjalankannya.