Reguläre Ausdrücke in Kotlin

Reguläre Ausdrücke in Kotlin

1. Einführung

Wir könnenregular expressions in so ziemlich jeder Art von Software verwenden (oder missbrauchen), von schnellen Skripten bis zu unglaublich komplexen Anwendungen.

In diesem Artikel erfahren Sie, wie Sie reguläre Ausdrücke in Kotlin verwenden.

Wir werden nicht über die Syntax regulärer Ausdrücke sprechen. Um den Artikel angemessen zu befolgen, ist im Allgemeinen eine Vertrautheit mit regulären Ausdrücken erforderlich, und es wird empfohlen, dieJava Pattern syntax genau zu kennen.

2. Konfiguration

Während reguläre Ausdrücke nicht Teil der Kotlin-Sprache sind, werden sie mit der Standardbibliothek geliefert.

Wir haben es wahrscheinlich schon als Abhängigkeit von unserem Projekt:


    org.jetbrains.kotlin
    kotlin-stdlib
    1.2.21

Wir können die neueste Version vonkotlin-stdlib auf Maven Central finden.

3. Erstellen eines Objekts mit regulären Ausdrücken

Reguläre Ausdrücke sind Instanzen der Klassekotlin.text.Regex. Wir können eine auf verschiedene Arten erstellen.

Eine Möglichkeit besteht darin, den KonstruktorRegexaufzurufen:

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

oder wir können die MethodetoRegex fürString: aufrufen

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

Schließlich können wir eine statische Factory-Methode verwenden:

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

Abgesehen von einem Unterschied, der im nächsten Abschnitt erläutert wird, entsprechen diese Optionen den persönlichen Vorlieben. Denken Sie daran, konsequent zu sein!

Tipp: Reguläre Ausdrücke enthalten häufig Zeichen, die inString Literalen als Escape-Sequenzen interpretiert werden. Wir können also roheStrings verwenden, um mehrere Ebenen der Flucht zu vergessen:

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

3.1. Übereinstimmende Optionen

Sowohl der KonstruktorRegexals auch die MethodetoRegexermöglichen es uns, eine einzelne zusätzliche Option oder eine Menge anzugeben:

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))

Optionen werden in der KlasseRegexOptionaufgelistet, die wir im obigen Beispiel bequem statisch importiert haben:

  • IGNORE_CASE – ermöglicht die Übereinstimmung ohne Berücksichtigung der Groß- und Kleinschreibung

  • MULTILINE - Ändert die Bedeutung von^ und$ (siehePattern)

  • LITERAL - bewirkt, dass Metazeichen oder Escape-Sequenzen im Muster keine besondere Bedeutung erhalten

  • UNIX_LINES - In diesem Modus wird nur als Leitungsterminator erkannt

  • COMMENTS - erlaubt Leerzeichen und Kommentare im Muster

  • DOT_MATCHES_ALL - bewirkt, dass der Punkt mit einem beliebigen Zeichen übereinstimmt, einschließlich eines Zeilenabschlusses

  • CANON_EQ - ermöglicht die Äquivalenz durch kanonische Zerlegung (siehePattern)

4. Passend

We use regular expressions primarily to match input Strings, und manchmal, um Teile davon zu extrahieren oder zu ersetzen.

Wir werden uns nun detailliert die Methoden ansehen, die von KotlinsRegex-Klasse zum Abgleichen vonStrings.angeboten werden

4.1. Überprüfen von Teil- oder Gesamtübereinstimmungen

In diesen Anwendungsfällen sind wir anknowing whether a String or a portion of a String satisfies our regular expression. interessiert

Wenn wir nur eine teilweise Übereinstimmung benötigen, können wircontainsMatchIn verwenden:

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

assertTrue(regex.containsMatchIn("xabcdy"))

Wenn wir möchten, dass stattdessen die gesamtenString übereinstimmen, verwenden wirmatches:

assertTrue(regex.matches("abcd"))

Beachten Sie, dass wirmatches auch als Infix-Operator verwenden können:

assertFalse(regex matches "xabcdy")

4.2. Übereinstimmende Komponenten extrahieren

In diesen Anwendungsfällen möchten wirto match a String against a regular expression and extract parts of the String.

Möglicherweise möchten wir die gesamtenString: anpassen

val matchResult = regex.matchEntire("abbccbbd")

Oder wir möchten vielleicht den ersten Teilstring finden, der passt:

val matchResult = regex.find("abcbabbd")

Oder um alle passenden Teilzeichenfolgen auf einmal zu finden, alsSet:

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

In beiden Fällen ist das Ergebnis bei erfolgreicher Übereinstimmung eine oder mehrere Instanzen der KlasseMatchResult. Im nächsten Abschnitt erfahren Sie, wie Sie es verwenden.

Wenn die Übereinstimmung nicht erfolgreich ist, geben diese Methoden stattdessennull oder die leerenSet im Fall vonfindAll zurück.

4.3. DieMatchResult Klasse

Instanzen der KlasseMatchResultrepräsentieren erfolgreiche Übereinstimmungen einer Eingabezeichenfolge mit einem regulären Ausdruck. entweder vollständige oder teilweise Übereinstimmungen (siehe vorherigen Abschnitt).

Als solche haben sievalue, was dem übereinstimmendenString oder Teilstring entspricht:

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

assertEquals("abcb", matchResult.value)

Und sie haben einrange von Indizes, um anzuzeigen, welcher Teil der Eingabe übereinstimmte:

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

4.4. Gruppen und Destrukturierung

Wir können auch Gruppen (übereinstimmende Teilzeichenfolgen) ausMatchResult Instanzen extrahieren.

Wir können sie alsStrings: erhalten

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

Oder wir können sie auch alsMatchGroup Objekte anzeigen, die ausvalue undrange bestehen:

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

Die Gruppe mit dem Index 0 ist immer die gesamte übereinstimmendeString.. Indizes größer als 0 stellen stattdessen Gruppen im regulären Ausdruck dar, die durch Klammern begrenzt sind, wie z. B.([bc]+) in unserem Beispiel.

Wir können auchMatchResult-Instanzen in einer Zuweisungsanweisung zerstören:

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. Mehrere Übereinstimmungen

MatchResult hat auch einenext-Methode, mit der wirto obtain the next match of the input String against the regular expression verwenden können, falls vorhanden:

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)

Wie wir sehen können, gibtnext null zurück, wenn keine Übereinstimmungen mehr vorhanden sind.

5. Ersetzen

Eine andere häufige Verwendung von regulären Ausdrücken istreplacing matching substrings with other Strings.

Zu diesem Zweck stehen in der Standardbibliothek zwei Methoden zur Verfügung.

Eine,replace, dient zum Ersetzen aller Vorkommen einer übereinstimmendenString:

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)

Das andere,replaceFirst, dient nur zum Ersetzen des ersten Auftretens:

val shiny = regex.replaceFirst(beautiful, "rainbow")

assertEquals("Roses are rainbow, Violets are blue", shiny)

5.1. Komplexe Ersetzungen

Fürmore advanced scenarios gibt unsRegex immer noch das, was wir brauchen, wenn wir Übereinstimmungen nicht durch konstanteStrings, ersetzen möchten, sondern stattdessento apply a transformation.

Geben Sie die Überlastung vonreplaceein, die geschlossen wird:

val reallyBeautiful = regex.replace(beautiful) {
    m -> m.value.toUpperCase() + "!"
}

assertEquals("Roses are RED!, Violets are BLUE!", reallyBeautiful)

Wie wir sehen können, können wir für jede Übereinstimmung einen ErsatzString unter Verwendung dieser Übereinstimmung berechnen.

6. Aufteilen

Schließlich möchten wir vielleichtto split a String into a list of substrings according to a regular expression.. Auch hier hat KotlinsRegex uns abgedeckt:

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

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

Hier stimmt der reguläre Ausdruck mit einem oder mehreren Nicht-Wort-Zeichen überein, sodass das Ergebnis der Aufteilungsoperation eine Liste von Wörtern ist.

Wir können auch die Länge der resultierenden Liste begrenzen:

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

7. Java-Interoperabilität

Wenn wir unseren regulären Ausdruck an Java-Code oder eine andere JVM-Sprach-API übergeben müssen, die eine Instanz vonjava.util.regex.Pattern erwartet, können wir einfach unsereRegex konvertieren:

regex.toPattern()

8. Schlussfolgerungen

In diesem Artikel haben wir die Unterstützung regulärer Ausdrücke in der Kotlin-Standardbibliothek untersucht.

Weitere Informationen finden Sie unterthe Kotlin reference.

Die Implementierung all dieser Beispiele und Codefragmente finden Sie inthe GitHub project - dies ist ein Maven-Projekt, daher sollte es einfach zu importieren und auszuführen sein.