コトリンの正規表現

Kotlinの正規表現

1. 前書き

クイックスクリプトから非常に複雑なアプリケーションまで、ほぼすべての種類のソフトウェアでregular expressionsの使用(または悪用)を見つけることができます。

この記事では、Kotlinで正規表現を使用する方法を説明します。

正規表現の構文については説明しません。一般に、記事を適切に理解するには正規表現に精通している必要があり、特にJava Pattern syntaxの知識が推奨されます。

2. セットアップ

正規表現はKotlin言語の一部ではありませんが、標準ライブラリが付属しています。

おそらく私たちのプロジェクトの依存関係としてすでにそれを持っています:


    org.jetbrains.kotlin
    kotlin-stdlib
    1.2.21

Maven Centralで最新バージョンのkotlin-stdlibを見つけることができます。

3. 正規表現オブジェクトの作成

正規表現は、kotlin.text.Regexクラスのインスタンスです。 いくつかの方法で作成できます。

可能性は、Regexコンストラクターを呼び出すことです。

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

または、String:toRegexメソッドを呼び出すことができます

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

最後に、静的ファクトリーメソッドを使用できます。

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

次のセクションで説明する違いを除けば、これらのオプションは個人の好みと同等であり、量も同じです。 一貫性があることを忘れないでください!

ヒント:正規表現には、Stringリテラルのエスケープシーケンスとして解釈される文字が含まれていることがよくあります。 したがって、生のStringsを使用して、複数レベルのエスケープを忘れることができます。

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

3.1. マッチングオプション

RegexコンストラクターとtoRegexメソッドの両方で、単一の追加オプションまたはセットを指定できます。

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

オプションはRegexOptionクラスに列挙されています。これは、上記の例で静的にインポートしたものです。

  • IGNORE_CASE –は、大文字と小文字を区別しないマッチングを有効にします

  • MULTILINE^および$の意味を変更します(Pattern)を参照)

  • LITERAL –パターン内のメタ文字またはエスケープシーケンスに特別な意味が与えられないようにします

  • UNIX_LINES –このモードでは、 のみがラインターミネータとして認識されます。

  • COMMENTS –パターン内の空白とコメントを許可します

  • DOT_MATCHES_ALL –ドットをラインターミネータを含む任意の文字と一致させます

  • CANON_EQ –正規分解による等価性を有効にします(Patternを参照)

4. マッチング

We use regular expressions primarily to match input Strings,であり、場合によってはそれらの一部を抽出または置換します。

ここで、Strings.を照合するためにKotlinのRegexクラスによって提供されるメソッドについて詳しく見ていきます。

4.1. 部分一致または全体一致の確認

これらのユースケースでは、knowing whether a String or a portion of a String satisfies our regular expression.に関心があります

部分一致のみが必要な場合は、containsMatchInを使用できます。

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

assertTrue(regex.containsMatchIn("xabcdy"))

代わりにString全体を一致させたい場合は、matchesを使用します。

assertTrue(regex.matches("abcd"))

matchesを中置演算子としても使用できることに注意してください。

assertFalse(regex matches "xabcdy")

4.2. 一致するコンポーネントの抽出

これらのユースケースでは、to match a String against a regular expression and extract parts of the String.が必要です

String:全体を一致させたい場合があります

val matchResult = regex.matchEntire("abbccbbd")

または、一致する最初の部分文字列を検索することもできます。

val matchResult = regex.find("abcbabbd")

または、一致するすべての部分文字列をSetとして一度に検索することもできます。

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

いずれの場合も、一致が成功すると、結果はMatchResultクラスの1つ以上のインスタンスになります。 次のセクションでは、その使用方法を説明します。

一致が成功しなかった場合、代わりに、これらのメソッドはnullを返すか、findAllの場合は空のSetを返します。

4.3. MatchResultクラス

MatchResultクラスのインスタンスは、正規表現に対する一部の入力文字列の正常な一致を表します。完全一致または部分一致のいずれか(前のセクションを参照)。

そのため、valueがあります。これは、一致するStringまたは部分文字列です。

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

assertEquals("abcb", matchResult.value)

また、入力のどの部分が一致したかを示すインデックスのrangeがあります。

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

4.4. グループと破壊

MatchResultインスタンスからグループ(一致した部分文字列)を抽出することもできます。

それらをStrings:として取得できます

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

または、それらをvaluerangeで構成されるMatchGroupオブジェクトとして表示することもできます。

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

インデックス0のグループは、常に一致するString.インデックス全体が0より大きいものです。代わりに、この例の([bc]+)のように、括弧で区切られた正規表現のグループを表します。

代入ステートメントでMatchResultインスタンスを分解することもできます。

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. 複数の一致

MatchResultには、to obtain the next match of the input String against the regular expressionを使用できるnextメソッドもあります。

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)

ご覧のとおり、一致するものがなくなると、nextはnullを返します。

5. 交換する

正規表現のもう1つの一般的な使用法は、replacing matching substrings with other Strings.です。

この目的のために、標準ライブラリですぐに利用できる2つのメソッドがあります。

1つ、replaceは、一致する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)

もう1つのreplaceFirstは、最初のオカレンスのみを置き換えるためのものです。

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

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

5.1. 複雑な交換

more advanced scenariosの場合、一致を定数Strings,に置き換えたくないが、代わりにto apply a transformationが必要な場合でも、Regexは必要なものを提供します。

クロージャを取るreplaceオーバーロードを入力します。

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

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

ご覧のとおり、一致ごとに、その一致を使用して置換Stringを計算できます。

6. 分割

最後に、to split a String into a list of substrings according to a regular expression.が必要になる場合があります。ここでも、KotlinのRegexでカバーされています。

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

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

ここでは、正規表現は単語以外の1つ以上の文字と一致するため、分割操作の結果は単語のリストになります。

結果のリストの長さに制限を設けることもできます:

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

7. Javaの相互運用性

正規表現をJavaコード、またはjava.util.regex.Patternのインスタンスを期待する他のJVM言語APIに渡す必要がある場合は、Regexを単純に変換できます。

regex.toPattern()

8. 結論

この記事では、Kotlin標準ライブラリでの正規表現のサポートについて説明しました。

詳細については、the Kotlin referenceを参照してください。

これらすべての例とコードスニペットの実装はthe GitHub projectにあります。これはMavenプロジェクトであるため、そのままインポートして実行するのは簡単です。