1. 序章

正規表現の使用(または悪用)は、クイックスクリプトから非常に複雑なアプリケーションまで、ほぼすべての種類のソフトウェアで見られます。

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

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

2. 設定

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

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

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib</artifactId>
    <version>1.2.21</version>
</dependency>

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

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 ^および$の意味を変更します(パターンを参照)
  • LITERAL –パターン内のメタ文字またはエスケープシーケンスに特別な意味が与えられないようにします
  • UNIX_LINES –このモードでは、 \nのみがラインターミネータとして認識されます
  • COMMENTS –パターン内の空白とコメントを許可します
  • DOT_MATCHES_ALL –ドットをラインターミネーターを含む任意の文字と一致させます
  • CANON_EQ –正規分解による等価化を有効にします(パターンを参照)

4. マッチング

正規表現は、主に入力文字列を照合するために使用し、、場合によってはそれらの一部を抽出または置換するために使用します。

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

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

これらのユースケースでは、文字列または文字列の一部が正規表現を満たすかどうかを知ることに関心があります。

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

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

assertTrue(regex.containsMatchIn("xabcdy"))

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

assertTrue(regex.matches("abcd"))

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

assertFalse(regex matches "xabcdy")

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

これらのユースケースでは、で文字列を正規表現と照合し、文字列の一部を抽出する必要があります。

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)

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

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

インデックス0のグループは、常に一致する文字列全体です。 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 には、 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つの一般的な使用法は、一致する部分文字列を他の文字列に置き換えることです。

この目的のために、標準ライブラリですぐに利用できる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. 複雑な交換

より高度なシナリオの場合、一致を定数文字列に置き換えたくないが、に変換を適用したい場合は、正規表現[ X174X]それでも必要なものを提供してくれます。

置換オーバーロードを入力して、クロージャーを取得します。

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

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

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

6. 分割

最後に、で文字列を正規表現に従ってサブ文字列のリストに分割したい場合があります。繰り返しになりますが、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標準ライブラリでの正規表現のサポートについて説明しました。

詳細については、Kotlinリファレンスを参照してください。

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