Expressions régulières en Kotlin

Expressions régulières en Kotlin

1. introduction

Nous pouvons trouver l'utilisation (ou l'abus) deregular expressions dans à peu près tous les types de logiciels, des scripts rapides aux applications incroyablement complexes.

Dans cet article, nous allons voir comment utiliser les expressions régulières dans Kotlin.

Nous ne discuterons pas de la syntaxe des expressions régulières; une familiarité avec les expressions régulières, en général, est nécessaire pour bien suivre l'article, et la connaissance desJava Pattern syntax spécifiquement est recommandée.

2. Installer

Si les expressions régulières ne font pas partie du langage Kotlin, elles sont livrées avec sa bibliothèque standard.

Nous l'avons probablement déjà en dépendance de notre projet:


    org.jetbrains.kotlin
    kotlin-stdlib
    1.2.21

Nous pouvons trouver la dernière version dekotlin-stdlib sur Maven Central.

3. Création d'un objet d'expression régulière

Les expressions régulières sont des instances de la classekotlin.text.Regex. Nous pouvons en créer un de plusieurs manières.

Une possibilité est d'appeler le constructeurRegex:

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

ou on peut appeler la méthodetoRegex sur unString:

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

Enfin, nous pouvons utiliser une méthode de fabrique statique:

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

À l'exception d'une différence expliquée dans la section suivante, ces options sont équivalentes et équivalentes aux préférences personnelles. Rappelez-vous juste d'être cohérent!

Astuce: les expressions régulières contiennent souvent des caractères qui seraient interprétés comme des séquences d'échappement dans les littérauxString. On peut donc utiliser desStrings bruts pour oublier plusieurs niveaux d'échappement:

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

3.1. Options correspondantes

Le constructeurRegex et la méthodetoRegex nous permettent de spécifier une seule option supplémentaire ou un ensemble:

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

Les options sont énumérées dans la classeRegexOption, que nous avons importée statiquement de manière pratique dans l'exemple ci-dessus:

  • IGNORE_CASE – active la correspondance insensible à la casse

  • MULTILINE - change la signification de^ et$ (voirPattern)

  • LITERAL - fait que les métacaractères ou les séquences d'échappement dans le modèle n'ont pas de signification particulière

  • UNIX_LINES - dans ce mode, seul le est reconnu comme terminateur de ligne

  • COMMENTS - autorise les espaces et les commentaires dans le motif

  • DOT_MATCHES_ALL - fait correspondre le point à n'importe quel caractère, y compris une terminaison de ligne

  • CANON_EQ - active l'équivalence par décomposition canonique (voirPattern)

4. Correspondant à

We use regular expressions primarily to match input Strings, et parfois pour en extraire ou en remplacer des parties.

Nous allons maintenant examiner en détail les méthodes proposées par la classeRegex de Kotlin pour faire correspondre lesStrings.

4.1. Vérification des correspondances partielles ou totales

Dans ces cas d'utilisation, nous sommes intéressés parknowing whether a String or a portion of a String satisfies our regular expression.

Si nous n'avons besoin que d'une correspondance partielle, nous pouvons utilisercontainsMatchIn:

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

assertTrue(regex.containsMatchIn("xabcdy"))

Si nous voulons que lesString entiers correspondent à la place, nous utilisonsmatches:

assertTrue(regex.matches("abcd"))

Notez que nous pouvons également utilisermatches comme opérateur d'infixe:

assertFalse(regex matches "xabcdy")

4.2. Extraction des composants correspondants

Dans ces cas d'utilisation, nous voulonsto match a String against a regular expression and extract parts of the String.

Nous pourrions vouloir faire correspondre lesString: entiers

val matchResult = regex.matchEntire("abbccbbd")

Ou nous pourrions vouloir trouver la première sous-chaîne qui correspond à:

val matchResult = regex.find("abcbabbd")

Ou peut-être pour trouver toutes les sous-chaînes correspondantes à la fois, sous forme deSet:

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

Dans les deux cas, si la correspondance est réussie, le résultat sera une ou plusieurs instances de la classeMatchResult. Dans la section suivante, nous verrons comment l'utiliser.

Si la correspondance échoue, à la place, ces méthodes renvoientnull ou lesSet vides dans le cas defindAll.

4.3. La classeMatchResult

Les instances de la classeMatchResult représentent des correspondances réussies d'une chaîne d'entrée avec une expression régulière; correspondances complètes ou partielles (voir la section précédente).

En tant que tels, ils ont unvalue, qui est leString ou la sous-chaîne correspondant:

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

assertEquals("abcb", matchResult.value)

Et ils ont unrange d'indices pour indiquer quelle partie de l'entrée correspond:

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

4.4. Groupes et destruction

Nous pouvons également extraire des groupes (sous-chaînes correspondantes) à partir des instancesMatchResult.

Nous pouvons les obtenir enStrings:

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

Ou nous pouvons également les visualiser comme des objetsMatchGroup constitués d'unvalue et d'unrange:

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

Le groupe d'index 0 est toujours la totalité des indicesString. correspondants supérieurs à 0, à la place, représentent des groupes dans l'expression régulière, délimités par des parenthèses, tels que([bc]+) dans notre exemple.

Nous pouvons également déstructurer les instancesMatchResult dans une instruction d'affectation:

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. Matchs multiples

MatchResult a également une méthodenext que nous pouvons utiliserto obtain the next match of the input String against the regular expression, s'il y en a:

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)

Comme nous pouvons le voir,next renvoie null lorsqu'il n'y a plus de correspondances.

5. Remplacement

Une autre utilisation courante des expressions régulières estreplacing matching substrings with other Strings.

À cette fin, nous avons deux méthodes facilement disponibles dans la bibliothèque standard.

L'un,replace, sert à remplacer toutes les occurrences d'unString: correspondant

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)

L'autre,replaceFirst, sert à remplacer uniquement la première occurrence:

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

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

5.1. Remplacements complexes

Pourmore advanced scenarios, lorsque nous ne voulons pas remplacer les correspondances par la constanteStrings, mais que nous voulons plutôtto apply a transformation,Regex nous donne toujours ce dont nous avons besoin.

Entrez la surcharge dereplace prenant une fermeture:

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

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

Comme nous pouvons le voir, pour chaque correspondance, nous pouvons calculer unString de remplacement en utilisant cette correspondance.

6. Scission

Enfin, nous pourrions vouloirto split a String into a list of substrings according to a regular expression. Encore une fois, lesRegex de Kotlin nous ont couverts:

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

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

Ici, l'expression régulière correspond à un ou plusieurs caractères autres que des mots, le résultat de l'opération de division est donc une liste de mots.

Nous pouvons également limiter la longueur de la liste résultante:

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

7. Interopérabilité Java

Si nous devons transmettre notre expression régulière au code Java, ou à une autre API de langage JVM qui attend une instance dejava.util.regex.Pattern, nous pouvons simplement convertir nosRegex:

regex.toPattern()

8. Conclusions

Dans cet article, nous avons examiné la prise en charge des expressions régulières dans la bibliothèque standard Kotlin.

Pour plus d'informations, voirthe Kotlin reference.

L'implémentation de tous ces exemples et extraits de code se trouve dansthe GitHub project - il s'agit d'un projet Maven, il devrait donc être facile à importer et à exécuter tel quel.