Ungkapan Biasa di Kotlin

1. Pengenalan

Kita dapat menggunakan (atau penyalahgunaan) ungkapan biasa dalam hampir semua jenis perisian, dari skrip cepat hingga aplikasi yang sangat rumit.

Dalam artikel ini, kita akan melihat cara menggunakan ungkapan biasa di Kotlin.

Kami tidak akan membincangkan sintaks ungkapan biasa; keakraban dengan ungkapan biasa, secara umum, diperlukan untuk mengikuti artikel dengan cukup, dan pengetahuan mengenai sintaks Pola Java secara khusus dianjurkan.

2. Persediaan

Walaupun ungkapan biasa bukan merupakan bagian dari bahasa Kotlin, kata-kata itu disertakan dengan perpustakaan standardnya.

Kami mungkin sudah memilikinya sebagai pergantungan projek kami:

 org.jetbrains.kotlin kotlin-stdlib 1.2.21 

Kita dapat mencari kotlin-stdlib versi terbaru di Maven Central.

3. Membuat Objek Ekspresi Biasa

Ungkapan biasa adalah contoh kelas kotlin.text.Regex . Kita boleh membuatnya dengan beberapa cara.

Kemungkinan adalah dengan memanggil pembangun Regex :

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

atau kita boleh memanggil kaedah toRegex pada String:

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

Akhirnya, kita boleh menggunakan kaedah kilang statik:

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

Simpan dari perbezaan yang dijelaskan di bahagian seterusnya, pilihan ini setara dan sesuai dengan pilihan peribadi. Ingatlah untuk sentiasa konsisten!

Petua: ungkapan biasa sering mengandungi watak-watak yang akan ditafsirkan sebagai urutan pelarian dalam literal String . Oleh itu, kita dapat menggunakan String mentah untuk melupakan pelbagai tahap melarikan diri:

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

3.1. Pilihan Padanan

Baik konstruktor Regex dan kaedah toRegex membolehkan kami menentukan satu pilihan 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))

Pilihan dihitung dalam kelas RegexOption , yang kami impor secara statik dengan mudah dalam contoh di atas:

  • IGNORE_CASE - membolehkan pemadanan tidak peka huruf besar-kecil
  • MULTILINE - mengubah makna ^ dan $ (lihat Corak)
  • LITERAL - menyebabkan metakarakter atau urutan pelarian dalam corak tidak diberi makna khusus
  • UNIX_LINES - dalam mod ini, hanya \ n yang dikenali sebagai penamat talian
  • KOMEN - membenarkan ruang kosong dan komen dalam corak
  • DOT_MATCHES_ALL - menyebabkan titik sesuai dengan mana-mana watak, termasuk penamat garis
  • CANON_EQ - membolehkan kesetaraan dengan penguraian kanonik (lihat Corak)

4. Memadankan

Kami menggunakan ungkapan biasa terutamanya untuk memadankan String input , dan kadang-kadang untuk mengekstrak atau mengganti bahagiannya.

Kami sekarang akan melihat secara terperinci kaedah yang ditawarkan oleh kelas Regex Kotlin untuk memadankan Strings.

4.1. Memeriksa Sepadan atau Sepadan

Dalam kes penggunaan ini, kami berminat untuk mengetahui sama ada String atau sebahagian String memenuhi ungkapan biasa kami.

Sekiranya kita hanya memerlukan pertandingan separa, kita boleh menggunakan mengandungMatchIn :

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

Sekiranya kita mahu keseluruhan String sesuai, kita menggunakan padanan :

assertTrue(regex.matches("abcd"))

Perhatikan bahawa kami juga dapat menggunakan perlawanan sebagai pengendali infix:

assertFalse(regex matches "xabcdy")

4.2. Mengeluarkan Komponen Pemadanan

Dalam kes penggunaan ini, kami ingin memadankan String dengan ungkapan biasa dan mengekstrak bahagian String.

Kami mungkin mahu memadankan keseluruhan String:

val matchResult = regex.matchEntire("abbccbbd")

Atau kita mungkin mahu mencari substring pertama yang sesuai:

val matchResult = regex.find("abcbabbd")

Atau mungkin untuk mencari semua substring yang sepadan sekaligus, sebagai Set :

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

Dalam mana-mana keadaan, jika pertandingan berjaya, hasilnya akan menjadi satu atau lebih contoh kelas MatchResult . Di bahagian seterusnya, 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

Sekiranya kita perlu menyampaikan ungkapan biasa kita ke kod Java, atau beberapa API bahasa JVM lain yang mengharapkan contoh java.util.regex.Pattern , kita boleh menukar Regex kita :

regex.toPattern()

8. Kesimpulan

Dalam artikel ini, kami telah memeriksa sokongan ekspresi biasa di perpustakaan standard Kotlin.

Untuk maklumat lebih lanjut, lihat rujukan Kotlin.

Pelaksanaan semua contoh dan coretan kod ini boleh didapati di projek GitHub - ini adalah projek Maven, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.