Regulære udtryk i Kotlin

1. Introduktion

Vi kan finde brug (eller misbrug) af regulære udtryk i stort set alle slags software, fra hurtige scripts til utroligt komplekse applikationer.

I denne artikel vil vi se, hvordan man bruger regulære udtryk i Kotlin.

Vi vil ikke diskutere syntaks for regulært udtryk; Der kræves generelt fortrolighed med regulære udtryk for at følge artiklen tilstrækkeligt, og viden om Java-mønster-syntaksen anbefales specifikt.

2. Opsætning

Mens regulære udtryk ikke er en del af Kotlin-sproget, kommer de med dets standardbibliotek.

Vi har sandsynligvis allerede det som en afhængighed af vores projekt:

 org.jetbrains.kotlin kotlin-stdlib 1.2.21 

Vi kan finde den nyeste version af kotlin-stdlib på Maven Central.

3. Oprettelse af et regulært udtryksobjekt

Regulære udtryk er forekomster af kotlin.text.Regex klasse. Vi kan oprette en på flere måder.

En mulighed er at ringe til Regex konstruktør:

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

eller vi kan ringe til toRegex metode på en Snor:

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

Endelig kan vi bruge en statisk fabriksmetode:

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

Bortset fra en forskel, der er forklaret i det næste afsnit, svarer disse muligheder til personligt. Husk bare at være konsekvent!

Tip: Regulære udtryk indeholder ofte tegn, der vil blive fortolket som flugtsekvenser i Snor bogstaver. Vi kan således bruge rå Strenge at glemme flere niveauer for at flygte:

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

3.1. Matchende muligheder

Både Regex konstruktør og toRegex metode tillader os at specificere en enkelt ekstra mulighed eller et sæt:

Regex ("a (b | c) + d?", CANON_EQ) Regex ("a (b | c) + d?", SetOf (DOT_MATCHES_ALL, KOMMENTARER)) "a (b | c) + d?". ToRegex (MULTILINE) "a (b | c) + d?". ToRegex (setOf (IGNORE_CASE, KOMMENTARER, UNIX_LINES))

Indstillinger er opregnet i RegexOption klasse, som vi bekvemt importerede statisk i eksemplet ovenfor:

  • IGNORE_CASE - muliggør case-ufølsom matchning
  • MULTILINE - ændrer betydningen af ^ og $ (se Mønster)
  • BOGSTAVELIG - forårsager, at metategn eller flugtsekvenser i mønsteret ikke får nogen særlig betydning
  • UNIX_LINES - i denne tilstand, kun \ n er anerkendt som en linjeterminator
  • KOMMENTARER - tillader mellemrum og kommentarer i mønsteret
  • DOT_MATCHES_ALL - får prikken til at matche ethvert tegn, inklusive en linjeterminator
  • CANON_EQ - muliggør ækvivalens ved kanonisk nedbrydning (se Mønster)

4. Matching

Vi bruger regulære udtryk primært til at matche input Strenge, og nogle gange for at udtrække eller udskifte dele af dem.

Vi ser nu detaljeret på de metoder, der tilbydes af Kotlin's Regex klasse til matchning Strenge.

4.1. Kontrol af delvise eller samlede matches

I disse brugssager er vi interesserede i at vide, om en Snor eller en del af en Snor tilfredsstiller vores regelmæssige udtryk.

Hvis vi kun har brug for en delvis kamp, ​​kan vi bruge indeholderMatchIn:

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

Hvis vi vil have helheden Snor for at matche i stedet bruger vi Tændstikker:

assertTrue (regex.matches ("abcd"))

Bemærk, at vi kan bruge Tændstikker som en infix-operatør også:

assertFalse (regex matcher "xabcdy")

4.2. Uddrag af matchende komponenter

I disse brugssager ønsker vi at matche en Snor mod et regulært udtryk og uddrag dele af Snor.

Vi vil måske matche det hele Snor:

val matchResult = regex.matchEntire ("abbccbbd")

Eller måske vil vi finde det første underlag, der matcher:

val matchResult = regex.find ("abcbabbd")

Eller måske for at finde alle de matchende underlag på én gang, som en Sæt:

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

I begge tilfælde, hvis kampen er vellykket, bliver resultatet en eller flere forekomster af MatchResult klasse. I det næste afsnit vil vi se, hvordan du bruger det.

Hvis kampen ikke lykkes, vender disse metoder i stedet tilbage nul eller det tomme Sæt i tilfælde af findAlle.

4.3. Det MatchResult Klasse

Forekomster af MatchResult klasse repræsenterer vellykkede matches af nogle inputstrenge mod et regulært udtryk; enten komplette eller delvise matches (se det foregående afsnit).

Som sådan har de en værdi, som er det matchede Snor eller substring:

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

Og de har en rækkevidde af indekser for at angive, hvilken del af input der blev matchet:

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

4.4. Grupper og destruktion

Vi kan også udtrække grupper (matchede understrenge) fra MatchResult tilfælde.

Vi kan få dem som Strenge:

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

Eller vi kan også se dem som MatchGroup genstande bestående af en værdi og en rækkevidde:

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

Gruppen med indeks 0 er altid hele matchet Snor. Indeks større end 0 repræsenterer i stedet grupper i det regulære udtryk, afgrænset med parenteser, såsom ([bc] +) i vores eksempel.

Vi kan også ødelægge MatchResult forekomster i en opgaveopgørelse:

val regex = "" "([\ w \ s] +) er (\ d +) år gammel" "". toRegex () val matchResult = regex.find ("Mickey Mouse er 95 år") val (navn, alder ) = matchResult !!. destrukturerede assertEquals ("Mickey Mouse", navn) assertEquals ("95", alder)

4.5. Flere kampe

MatchResult har også en Næste metode, som vi kan bruge for at opnå den næste match af input Snor mod det almindelige udtryk, hvis der er nogen:

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

Som vi kan se, Næste returnerer null, når der ikke er flere matches.

5. Udskiftning

En anden almindelig brug af regulære udtryk er udskiftning af matchende underlag med andre Strenge.

Til dette formål har vi to metoder, der er let tilgængelige i standardbiblioteket.

En, erstatte, er til erstatning af alle forekomster af en matching Snor:

val regex = "" "(rød | grøn | blå)" "". toRegex () val smuk = "Roser er røde, violer er blå" val grim = regex.replace (smukke, "mørke") hævderEquals ("Roser er mørke, violer er mørke ", dystre)

Den anden, erstatte første, er kun til erstatning for den første forekomst:

val skinnende = regex.replaceFirst (smuk, "regnbue") hævderEquals ("Roser er regnbue, violer er blå", skinnende)

5.1. Komplekse udskiftninger

Til mere avancerede scenarier, når vi ikke ønsker at erstatte tændstikker med konstant Strenge, men vi vil at anvende en transformation i stedet, Regex giver os stadig det, vi har brug for.

Gå ind i erstatte overbelastning ved at lukke:

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

Som vi kan se, kan vi beregne en erstatning for hver kamp Snor bruger den kamp.

6. Opdeling

Endelig vil vi måske have det at opdele en Snor i en liste over understrenge i henhold til et regulært udtryk. Igen, Kotlin's Regex har fået os dækket:

val regex = "" "\ W +" "". toRegex () val beautiful = "Roser er røde, violer er blå" assertEquals (listOf ("Roser", "er", "rød", "Violer", "er" , "blå"), regex.split (smuk))

Her matcher det regulære udtryk et eller flere tegn, der ikke er ord, så resultatet af opdeling er en liste med ord.

Vi kan også sætte en grænse for længden af ​​den resulterende liste:

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

7. Java-interoperabilitet

Hvis vi har brug for at videregive vores regulære udtryk til Java-kode eller en anden API til JVM-sprog, der forventer en forekomst af java.util.regex.Mønster, kan vi simpelthen konvertere vores Regex:

regex.toPattern ()

8. Konklusioner

I denne artikel har vi undersøgt understøttelsen af ​​regulært udtryk i Kotlin-standardbiblioteket.

For yderligere information, se Kotlin-referencen.

Implementeringen af ​​alle disse eksempler og kodestykker findes i GitHub-projektet - dette er et Maven-projekt, så det skal være let at importere og køre, som det er.