1. 概要

この記事では、Java Regex APIと、Javaプログラミング言語で正規表現を使用する方法について説明します。

正規表現の世界では、grep、Perl、Python、PHP、awkなど、さまざまなフレーバーから選択できます。

これは、あるプログラミング言語で機能する正規表現が別のプログラミング言語では機能しない可能性があることを意味します。 Javaの正規表現構文は、Perlの正規表現構文に最もよく似ています。

2. 設定

Javaで正規表現を使用するために、特別な設定は必要ありません。 JDKには、正規表現操作専用の特別なパッケージjava.util.regexが含まれています。 コードにインポートするだけです。

さらに、 java.lang.String クラスには、コードで一般的に使用する正規表現サポートも組み込まれています。

3. Java正規表現パッケージ

java.util.regex パッケージは、 Pattern、Matcher PatternSyntaxException:の3つのクラスで構成されています。

  • Patternオブジェクトはコンパイルされた正規表現です。 Pattern クラスは、パブリックコンストラクターを提供しません。 パターンを作成するには、最初にそのパブリック静的コンパイルメソッドの1つを呼び出す必要があります。これにより、パターンオブジェクトが返されます。 これらのメソッドは、最初の引数として正規表現を受け入れます。
  • Matcher オブジェクトはパターンを解釈し、入力Stringに対して一致操作を実行します。 また、パブリックコンストラクターも定義していません。 Patternオブジェクトでmatcherメソッドを呼び出すことにより、Matcherオブジェクトを取得します。
  • PatternSyntaxException オブジェクトは、正規表現パターンの構文エラーを示すチェックされていない例外です。

これらのクラスについて詳しく説明します。 ただし、最初にJavaで正規表現がどのように構築されるかを理解する必要があります。

別の環境の正規表現に既に精通している場合は、特定の違いが見つかる可能性がありますが、それらは最小限です。

4. 簡単な例

正規表現の最も単純なユースケースから始めましょう。前述のように、正規表現が文字列に適用されると、0回以上一致する可能性があります。

java.util.regex APIでサポートされているパターンマッチングの最も基本的な形式は、文字列リテラルマッチングです。 たとえば、正規表現が foo で、入力Stringfooの場合、 Strings は同一であるため、一致は成功します。

@Test
public void givenText_whenSimpleRegexMatches_thenCorrect() {
    Pattern pattern = Pattern.compile("foo");
    Matcher matcher = pattern.matcher("foo");
 
    assertTrue(matcher.find());
}

まず、静的な compile メソッドを呼び出して、使用するパターンを渡すことにより、Patternオブジェクトを作成します。

次に、 Matcher オブジェクトを作成し、Patternオブジェクトのmatcherメソッドを呼び出して、一致を確認するテキストを渡します。

その後、Matcherオブジェクトでメソッドfindを呼び出します。

find メソッドは、入力テキストを進め続け、すべての一致に対してtrueを返すため、これを使用して一致数も検索できます。

@Test
public void givenText_whenSimpleRegexMatchesTwice_thenCorrect() {
    Pattern pattern = Pattern.compile("foo");
    Matcher matcher = pattern.matcher("foofoo");
    int matches = 0;
    while (matcher.find()) {
        matches++;
    }
 
    assertEquals(matches, 2);
}

より多くのテストを実行するため、runTestというメソッドで一致数を見つけるためのロジックを抽象化できます。

public static int runTest(String regex, String text) {
    Pattern pattern = Pattern.compile(regex);
    Matcher matcher = pattern.matcher(text);
    int matches = 0;
    while (matcher.find()) {
        matches++;
    }
    return matches;
}

一致するものが0の場合、テストは失敗するはずです。そうでない場合は、合格するはずです。

5. メタキャラクター

メタ文字は、検索パターンにロジックを追加する方法で、パターンの照合方法に影響を与えます。 Java APIはいくつかのメタ文字をサポートしていますが、最も簡単なのはドット「」です。 これはどの文字にも一致します:

@Test
public void givenText_whenMatchesWithDotMetach_thenCorrect() {
    int matches = runTest(".", "foo");
    
    assertTrue(matches > 0);
}

正規表現fooがテキストfoofoofooに2回一致した前の例を考えてみましょう。 正規表現でドットメタ文字を使用した場合、2番目のケースでは2つの一致は得られません。

@Test
public void givenRepeatedText_whenMatchesOnceWithDotMetach_thenCorrect() {
    int matches= runTest("foo.", "foofoo");
 
    assertEquals(matches, 1);
}

正規表現のfooの後のドットに注意してください。 最後のドット部分は後の文字を意味するため、マッチャーはfooが前にあるすべてのテキストに一致します。 したがって、最初の foo を見つけた後、残りは任意の文字として表示されます。 そのため、一致するものは1つだけです。

APIは他のいくつかのメタ文字をサポートしています <([{\ ^-= $!|]})?*+。> これについては、この記事で詳しく説明します。

6. キャラクタークラス

公式のPatternクラス仕様を閲覧すると、サポートされている正規表現構造の概要がわかります。 文字クラスの下には、約6つの構成があります。

6.1. またはクラス

[abc]として構築されます。 セット内の要素のいずれかが一致します:

@Test
public void givenORSet_whenMatchesAny_thenCorrect() {
    int matches = runTest("[abc]", "b");
 
    assertEquals(matches, 1);
}

それらがすべてテキストに表示されている場合、順序に関係なく、それぞれが個別に照合されます。

@Test
public void givenORSet_whenMatchesAnyAndAll_thenCorrect() {
    int matches = runTest("[abc]", "cab");
 
    assertEquals(matches, 3);
}

Stringの一部として交互に使用することもできます。 次の例では、最初の文字をセットの各要素と交互に使用して異なる単語を作成すると、それらはすべて一致します。

@Test
public void givenORSet_whenMatchesAllCombinations_thenCorrect() {
    int matches = runTest("[bcr]at", "bat cat rat");
 
    assertEquals(matches, 3);
}

6.2. NORクラス

上記のセットは、最初の要素としてキャレットを追加することで無効になります。

@Test
public void givenNORSet_whenMatchesNon_thenCorrect() {
    int matches = runTest("[^abc]", "g");
 
    assertTrue(matches > 0);
}

別のケース:

@Test
public void givenNORSet_whenMatchesAllExceptElements_thenCorrect() {
    int matches = runTest("[^bcr]at", "sat mat eat");
 
    assertTrue(matches > 0);
}

6.3. レンジクラス

ハイフン(-)を使用して、一致するテキストが含まれる範囲を指定するクラスを定義できます。同様に、範囲を無効にすることもできます。

大文字の一致:

@Test
public void givenUpperCaseRange_whenMatchesUpperCase_
  thenCorrect() {
    int matches = runTest(
      "[A-Z]", "Two Uppercase alphabets 34 overall");
 
    assertEquals(matches, 2);
}

一致する小文字:

@Test
public void givenLowerCaseRange_whenMatchesLowerCase_
  thenCorrect() {
    int matches = runTest(
      "[a-z]", "Two Uppercase alphabets 34 overall");
 
    assertEquals(matches, 26);
}

大文字と小文字の両方を一致させる:

@Test
public void givenBothLowerAndUpperCaseRange_
  whenMatchesAllLetters_thenCorrect() {
    int matches = runTest(
      "[a-zA-Z]", "Two Uppercase alphabets 34 overall");
 
    assertEquals(matches, 28);
}

与えられた範囲の数字に一致する:

@Test
public void givenNumberRange_whenMatchesAccurately_
  thenCorrect() {
    int matches = runTest(
      "[1-5]", "Two Uppercase alphabets 34 overall");
 
    assertEquals(matches, 2);
}

別の範囲の数値との一致:

@Test
public void givenNumberRange_whenMatchesAccurately_
  thenCorrect2(){
    int matches = runTest(
      "3[0-5]", "Two Uppercase alphabets 34 overall");
  
    assertEquals(matches, 1);
}

6.4. ユニオンクラス

ユニオン文字クラスは、2つ以上の文字クラスを組み合わせた結果です。

@Test
public void givenTwoSets_whenMatchesUnion_thenCorrect() {
    int matches = runTest("[1-3[7-9]]", "123456789");
 
    assertEquals(matches, 6);
}

和集合は4、5、および6をスキップするため、上記のテストは9つの整数のうち6つにのみ一致します。

6.5. 交差点クラス

ユニオンクラスと同様に、このクラスは2つ以上のセット間で共通の要素を選択することで作成されます。 交差点を適用するには、 &&

@Test
public void givenTwoSets_whenMatchesIntersection_thenCorrect() {
    int matches = runTest("[1-6&&[3-9]]", "123456789");
 
    assertEquals(matches, 4);
}

2つのセットの共通部分には4つの要素しかないため、4つの一致が得られます。

6.6. 減算クラス

減算を使用して、1つ以上の文字クラスを無効にすることができます。たとえば、奇数の10進数のセットを照合します。

@Test
public void givenSetWithSubtraction_whenMatchesAccurately_thenCorrect() {
    int matches = runTest("[0-9&&[^2468]]", "123456789");
 
    assertEquals(matches, 5);
}

1,3,5,7,9のみが一致します。

7. 事前定義された文字クラス

Java正規表現APIは、事前定義された文字クラスも受け入れます。 上記の文字クラスの一部は、コードの直感性を低下させますが、短い形式で表現できます。 この正規表現のJavaバージョンの特別な側面の1つは、エスケープ文字です。

これからわかるように、ほとんどの文字はバックスラッシュで始まります。これはJavaで特別な意味を持っています。 これらをPatternクラスでコンパイルするには、先頭の円記号をエスケープする必要があります。 \d\\dになります。

[0-9] と同等の一致する数字:

@Test
public void givenDigits_whenMatches_thenCorrect() {
    int matches = runTest("\\d", "123");
 
    assertEquals(matches, 3);
}

[^ 0-9] と同等の、一致する非数字:

@Test
public void givenNonDigits_whenMatches_thenCorrect() {
    int mathces = runTest("\\D", "a6c");
 
    assertEquals(matches, 2);
}

一致する空白:

@Test
public void givenWhiteSpace_whenMatches_thenCorrect() {
    int matches = runTest("\\s", "a c");
 
    assertEquals(matches, 1);
}

一致する非空白:

@Test
public void givenNonWhiteSpace_whenMatches_thenCorrect() {
    int matches = runTest("\\S", "a c");
 
    assertEquals(matches, 2);
}

[a-zA-Z_0-9] と同等の単語文字の一致:

@Test
public void givenWordCharacter_whenMatches_thenCorrect() {
    int matches = runTest("\\w", "hi!");
 
    assertEquals(matches, 2);
}

単語以外の文字との一致:

@Test
public void givenNonWordCharacter_whenMatches_thenCorrect() {
    int matches = runTest("\\W", "hi!");
 
    assertEquals(matches, 1);
}

8. 量指定子

Java正規表現APIを使用すると、数量詞を使用することもできます。 これらを使用すると、一致するオカレンスの数を指定することで、一致の動作をさらに微調整できます。

テキストを0回または1回一致させるには、数量詞を使用します。

@Test
public void givenZeroOrOneQuantifier_whenMatches_thenCorrect() {
    int matches = runTest("\\a?", "hi");
 
    assertEquals(matches, 3);
}

または、Java正規表現APIでもサポートされている中括弧構文を使用することもできます。

@Test
public void givenZeroOrOneQuantifier_whenMatches_thenCorrect2() {
    int matches = runTest("\\a{0,1}", "hi");
 
    assertEquals(matches, 3);
}

この例では、長さゼロの一致の概念を紹介します。 数量詞の一致のしきい値がゼロの場合、すべての入力の最後にある空のStringを含むテキスト内のすべてに常に一致することがあります。 これは、入力が空の場合でも、長さがゼロの一致を1つ返すことを意味します。

これは、長さが2のS tring があるにもかかわらず、上記の例で3つの一致が得られる理由を説明しています。 3番目の一致は、長さがゼロの空のStringです。

テキストをゼロまたは無制限の時間に一致させるために、私たちは*数量詞を使用します。これは?:に似ています。

@Test
public void givenZeroOrManyQuantifier_whenMatches_thenCorrect() {
     int matches = runTest("\\a*", "hi");
 
     assertEquals(matches, 3);
}

サポートされている代替手段:

@Test
public void givenZeroOrManyQuantifier_whenMatches_thenCorrect2() {
    int matches = runTest("\\a{0,}", "hi");
 
    assertEquals(matches, 3);
}

差のある数量詞は+であり、一致するしきい値は1です。 必要なStringがまったく発生しない場合、一致するものはなく、長さがゼロのStringでもありません。

@Test
public void givenOneOrManyQuantifier_whenMatches_thenCorrect() {
    int matches = runTest("\\a+", "hi");
 
    assertFalse(matches);
}

サポートされている代替手段:

@Test
public void givenOneOrManyQuantifier_whenMatches_thenCorrect2() {
    int matches = runTest("\\a{1,}", "hi");
 
    assertFalse(matches);
}

Perlや他の言語の場合と同様に、中括弧構文を使用して、特定のテキストを何度も照合できます。

@Test
public void givenBraceQuantifier_whenMatches_thenCorrect() {
    int matches = runTest("a{3}", "aaaaaa");
 
    assertEquals(matches, 2);
}

上記の例では、 a が3回続けて表示された場合にのみ一致が発生するため、2つの一致が得られます。 ただし、次のテストでは、テキストが2回続けて表示されるだけなので、一致するものは得られません。

@Test
public void givenBraceQuantifier_whenFailsToMatch_thenCorrect() {
    int matches = runTest("a{3}", "aa");
 
    assertFalse(matches > 0);
}

ブレースで範囲を使用すると、範囲の上限から一致する貪欲な一致になります。

@Test
public void givenBraceQuantifierWithRange_whenMatches_thenCorrect() {
    int matches = runTest("a{2,3}", "aaaa");
 
    assertEquals(matches, 1);
}

少なくとも2つのオカレンスを指定しましたが、3つを超えないように指定したため、代わりに1つの一致が得られます。

ただし、APIを使用すると、マッチャーが範囲の下限から開始できるように、怠惰なアプローチまたは消極的なアプローチを指定できます。この場合、2つのオカレンスをaaおよびaaとしてマッチングします。

@Test
public void givenBraceQuantifierWithRange_whenMatchesLazily_thenCorrect() {
    int matches = runTest("a{2,3}?", "aaaa");
 
    assertEquals(matches, 2);
}

9. グループのキャプチャ

APIを使用すると、グループをキャプチャすることで複数の文字を1つのユニットとして扱うことができます

キャプチャグループに番号を付け、これらの番号を使用して逆参照できるようにします。

このセクションでは、Java正規表現APIでキャプチャグループを使用する方法の例をいくつか示します。

入力テキストに2桁の数字が隣接している場合にのみ一致するキャプチャグループを使用してみましょう。

@Test
public void givenCapturingGroup_whenMatches_thenCorrect() {
    int maches = runTest("(\\d\\d)", "12");
 
    assertEquals(matches, 1);
}

上記の一致に付けられた番号は1であり、後方参照を使用して、テキストの一致した部分の別のオカレンスと一致させたいことをマッチャーに通知します。 このように、代わりに:

@Test
public void givenCapturingGroup_whenMatches_thenCorrect2() {
    int matches = runTest("(\\d\\d)", "1212");
 
    assertEquals(matches, 2);
}

入力に2つの別々の一致がある場合、1つの一致を持つことができますが、逆参照を使用して入力の全長にまたがるように同じ正規表現の一致を伝播します。

@Test
public void givenCapturingGroup_whenMatchesWithBackReference_
  thenCorrect() {
    int matches = runTest("(\\d\\d)\\1", "1212");
 
    assertEquals(matches, 1);
}

同じ結果を達成するために、逆参照せずに正規表現を繰り返す必要がある場合:

@Test
public void givenCapturingGroup_whenMatches_thenCorrect3() {
    int matches = runTest("(\\d\\d)(\\d\\d)", "1212");
 
    assertEquals(matches, 1);
}

同様に、他の繰り返し回数の場合、逆参照により、マッチャーは入力を単一の一致と見なすことができます。

@Test
public void givenCapturingGroup_whenMatchesWithBackReference_
  thenCorrect2() {
    int matches = runTest("(\\d\\d)\\1\\1\\1", "12121212");
 
    assertEquals(matches, 1);
}

ただし、最後の桁も変更すると、一致は失敗します。

@Test
public void givenCapturingGroupAndWrongInput_
  whenMatchFailsWithBackReference_thenCorrect() {
    int matches = runTest("(\\d\\d)\\1", "1213");
 
    assertFalse(matches > 0);
}

エスケープバックスラッシュを忘れないことが重要です。これはJava構文では非常に重要です。

10. バウンダリーマッチャー

Java正規表現APIは、境界マッチングもサポートしています。 入力テキストのどこで一致が発生するかを正確に気にする場合、これが私たちが探しているものです。 前の例では、一致するものが見つかったかどうかだけを気にしました。

テキストの冒頭で必要な正規表現が真である場合にのみ一致させるために、キャレットを使用します ^。

テキストdogが最初に見つかるため、このテストは失敗します。

@Test
public void givenText_whenMatchesAtBeginning_thenCorrect() {
    int matches = runTest("^dog", "dogs are friendly");
 
    assertTrue(matches > 0);
}

次のテストは失敗します。

@Test
public void givenTextAndWrongInput_whenMatchFailsAtBeginning_
  thenCorrect() {
    int matches = runTest("^dog", "are dogs are friendly?");
 
    assertFalse(matches > 0);
}

テキストの最後に必要な正規表現が真である場合にのみ一致させるために、ドル文字を使用します $。 次の場合に一致が見つかります。

@Test
public void givenText_whenMatchesAtEnd_thenCorrect() {
    int matches = runTest("dog$", "Man's best friend is a dog");
 
    assertTrue(matches > 0);
}

そして、ここでは一致するものは見つかりません:

@Test
public void givenTextAndWrongInput_whenMatchFailsAtEnd_thenCorrect() {
    int matches = runTest("dog$", "is a dog man's best friend?");
 
    assertFalse(matches > 0);
}

必要なテキストが単語の境界で見つかった場合にのみ一致が必要な場合は、正規表現の最初と最後に \\b正規表現を使用します。

スペースは単語の境界です:

@Test
public void givenText_whenMatchesAtWordBoundary_thenCorrect() {
    int matches = runTest("\\bdog\\b", "a dog is friendly");
 
    assertTrue(matches > 0);
}

行頭の空の文字列も単語の境界です。

@Test
public void givenText_whenMatchesAtWordBoundary_thenCorrect2() {
    int matches = runTest("\\bdog\\b", "dog is man's best friend");
 
    assertTrue(matches > 0);
}

String の先頭、および1つのテキストと別のテキストの間のスペースが単語の境界を示すため、これらのテストは合格ですが、次のテストは反対のことを示しています。

@Test
public void givenWrongText_whenMatchFailsAtWordBoundary_thenCorrect() {
    int matches = runTest("\\bdog\\b", "snoop dogg is a rapper");
 
    assertFalse(matches > 0);
}

行に表示される2単語の文字は単語の境界をマークしませんが、正規表現の終わりを変更して単語以外の境界を探すことにより、通過させることができます。

@Test
public void givenText_whenMatchesAtWordAndNonBoundary_thenCorrect() {
    int matches = runTest("\\bdog\\B", "snoop dogg is a rapper");
    assertTrue(matches > 0);
}

11. パターンクラスメソッド

以前は、基本的な方法でPatternオブジェクトのみを作成していました。 ただし、このクラスには、 compile メソッドの別のバリアントがあり、パターンの一致方法に影響を与える正規表現引数と一緒にフラグのセットを受け入れます。

これらのフラグは、単純に抽象化された整数値です。 テストクラスのrunTestメソッドをオーバーロードして、3番目の引数としてフラグを取得できるようにします。

public static int runTest(String regex, String text, int flags) {
    pattern = Pattern.compile(regex, flags);
    matcher = pattern.matcher(text);
    int matches = 0;
    while (matcher.find()){
        matches++;
    }
    return matches;
}

このセクションでは、サポートされているさまざまなフラグとその使用方法について説明します。

Pattern.CANON_EQ

このフラグは、正規の同等性を有効にします。 指定すると、完全な正規分解が一致する場合にのみ、2つの文字が一致すると見なされます。

アクセント付きのUnicode文字éについて考えてみます。 その複合コードポイントはu00E9です。 ただし、Unicodeには、そのコンポーネント文字 e u0065 、およびアキュートアクセントu0301用の個別のコードポイントもあります。 この場合、複合文字u00E9は2文字シーケンスu0065uと区別できません。0301.

デフォルトでは、マッチングでは正規の同等性は考慮されません。

@Test
public void givenRegexWithoutCanonEq_whenMatchFailsOnEquivalentUnicode_thenCorrect() {
    int matches = runTest("\u00E9", "\u0065\u0301");
 
    assertFalse(matches > 0);
}

ただし、フラグを追加すると、テストは合格します。

@Test
public void givenRegexWithCanonEq_whenMatchesOnEquivalentUnicode_thenCorrect() {
    int matches = runTest("\u00E9", "\u0065\u0301", Pattern.CANON_EQ);
 
    assertTrue(matches > 0);
}

Pattern.CASE_INSENSITIVE

このフラグは、大文字と小文字を区別せずに照合を有効にします。 デフォルトでは、マッチングではケースが考慮されます。

@Test
public void givenRegexWithDefaultMatcher_whenMatchFailsOnDifferentCases_thenCorrect() {
    int matches = runTest("dog", "This is a Dog");
 
    assertFalse(matches > 0);
}

したがって、このフラグを使用して、デフォルトの動作を変更できます。

@Test
public void givenRegexWithCaseInsensitiveMatcher
  _whenMatchesOnDifferentCases_thenCorrect() {
    int matches = runTest(
      "dog", "This is a Dog", Pattern.CASE_INSENSITIVE);
 
    assertTrue(matches > 0);
}

同等の埋め込みフラグ式を使用して、同じ結果を得ることができます。

@Test
public void givenRegexWithEmbeddedCaseInsensitiveMatcher
  _whenMatchesOnDifferentCases_thenCorrect() {
    int matches = runTest("(?i)dog", "This is a Dog");
 
    assertTrue(matches > 0);
}

Pattern.COMMENTS

Java APIを使用すると、正規表現に#を使用してコメントを含めることができます。 これは、他のプログラマーにはすぐにはわからないかもしれない複雑な正規表現を文書化するのに役立ちます。

コメントフラグを使用すると、マッチャーは正規表現内の空白やコメントを無視し、パターンのみを考慮します。 デフォルトのマッチングモードでは、次のテストは失敗します。

@Test
public void givenRegexWithComments_whenMatchFailsWithoutFlag_thenCorrect() {
    int matches = runTest(
      "dog$  #check for word dog at end of text", "This is a dog");
 
    assertFalse(matches > 0);
}

これは、マッチャーがスペースと#文字を含む入力テキスト内の正規表現全体を検索するためです。 ただし、フラグを使用すると、余分なスペースが無視され、#で始まるすべてのテキストが各行で無視されるコメントとして表示されます。

@Test
public void givenRegexWithComments_whenMatchesWithFlag_thenCorrect() {
    int matches = runTest(
      "dog$  #check end of text","This is a dog", Pattern.COMMENTS);
 
    assertTrue(matches > 0);
}

これには、代替の埋め込みフラグ式もあります。

@Test
public void givenRegexWithComments_whenMatchesWithEmbeddedFlag_thenCorrect() {
    int matches = runTest(
      "(?x)dog$  #check end of text", "This is a dog");
 
    assertTrue(matches > 0);
}

Pattern.DOTALL

デフォルトでは、ドット「。」を使用します。 正規表現の式では、改行文字に遭遇するまで、入力Stringのすべての文字を照合します。

このフラグを使用すると、一致にはラインターミネータも含まれます。 次の例を使用すると、理解が深まります。 これらの例は少し異なります。 一致したStringに対してアサートすることに関心があるため、前の一致を返すmatchergroupメソッドを使用します。

まず、デフォルトの動作を確認します。

@Test
public void givenRegexWithLineTerminator_whenMatchFails_thenCorrect() {
    Pattern pattern = Pattern.compile("(.*)");
    Matcher matcher = pattern.matcher(
      "this is a text" + System.getProperty("line.separator") 
        + " continued on another line");
    matcher.find();
 
    assertEquals("this is a text", matcher.group(1));
}

ご覧のとおり、ラインターミネータの前の入力の最初の部分のみが一致します。

dotall モードでは、行末記号を含むテキスト全体が一致します。

@Test
public void givenRegexWithLineTerminator_whenMatchesWithDotall_thenCorrect() {
    Pattern pattern = Pattern.compile("(.*)", Pattern.DOTALL);
    Matcher matcher = pattern.matcher(
      "this is a text" + System.getProperty("line.separator") 
        + " continued on another line");
    matcher.find();
    assertEquals(
      "this is a text" + System.getProperty("line.separator") 
        + " continued on another line", matcher.group(1));
}

埋め込みフラグ式を使用して、dotallモードを有効にすることもできます。

@Test
public void givenRegexWithLineTerminator_whenMatchesWithEmbeddedDotall
  _thenCorrect() {
    
    Pattern pattern = Pattern.compile("(?s)(.*)");
    Matcher matcher = pattern.matcher(
      "this is a text" + System.getProperty("line.separator") 
        + " continued on another line");
    matcher.find();
 
    assertEquals(
      "this is a text" + System.getProperty("line.separator") 
        + " continued on another line", matcher.group(1));
}

Pattern.LITERAL

このモードでは、マッチャーはメタ文字、エスケープ文字、または正規表現構文に特別な意味を与えません。 このフラグがないと、マッチャーは次の正規表現を任意の入力Stringと照合します。

@Test
public void givenRegex_whenMatchesWithoutLiteralFlag_thenCorrect() {
    int matches = runTest("(.*)", "text");
 
    assertTrue(matches > 0);
}

これは、すべての例で見られたデフォルトの動作です。 ただし、このフラグを使用すると、マッチャーは(。*)を解釈する代わりに検索するため、一致は見つかりません。

@Test
public void givenRegex_whenMatchFailsWithLiteralFlag_thenCorrect() {
    int matches = runTest("(.*)", "text", Pattern.LITERAL);
 
    assertFalse(matches > 0);
}

ここで、必要な文字列を追加すると、テストに合格します。

@Test
public void givenRegex_whenMatchesWithLiteralFlag_thenCorrect() {
    int matches = runTest("(.*)", "text(.*)", Pattern.LITERAL);
 
    assertTrue(matches > 0);
}

リテラル解析を有効にするための埋め込みフラグ文字はありません。

Pattern.MULTILINE

デフォルトでは、^および$メタ文字は、入力String全体の最初と最後でそれぞれ完全に一致します。 マッチャーはラインターミネーターを無視します。

@Test
public void givenRegex_whenMatchFailsWithoutMultilineFlag_thenCorrect() {
    int matches = runTest(
      "dog$", "This is a dog" + System.getProperty("line.separator") 
      + "this is a fox");
 
    assertFalse(matches > 0);
}

マッチャーはString全体の最後でdogを検索しますが、 dog は文字列の最初の行の最後にあるため、一致は失敗します。

ただし、フラグを使用すると、マッチャーがラインターミネータを考慮に入れるようになったため、同じテストに合格します。 したがって、文字列 dog は、行が終了する直前に検出されるため、成功します。

@Test
public void givenRegex_whenMatchesWithMultilineFlag_thenCorrect() {
    int matches = runTest(
      "dog$", "This is a dog" + System.getProperty("line.separator") 
      + "this is a fox", Pattern.MULTILINE);
 
    assertTrue(matches > 0);
}

埋め込まれたフラグのバージョンは次のとおりです。

@Test
public void givenRegex_whenMatchesWithEmbeddedMultilineFlag_
  thenCorrect() {
    int matches = runTest(
      "(?m)dog$", "This is a dog" + System.getProperty("line.separator") 
      + "this is a fox");
 
    assertTrue(matches > 0);
}

12. マッチャークラスメソッド

このセクションでは、Matcherクラスのいくつかの便利なメソッドを見ていきます。 わかりやすくするために、機能ごとにグループ化します。

12.1. インデックスメソッド

インデックスメソッドは、入力Stringで一致が見つかった場所を正確に示す有用なインデックス値を提供します。 次のテストでは、入力Stringdogの一致の開始インデックスと終了インデックスを確認します。

@Test
public void givenMatch_whenGetsIndices_thenCorrect() {
    Pattern pattern = Pattern.compile("dog");
    Matcher matcher = pattern.matcher("This dog is mine");
    matcher.find();
 
    assertEquals(5, matcher.start());
    assertEquals(8, matcher.end());
}

12.2. 学習方法

スタディメソッドは入力Stringを調べ、パターンが見つかったかどうかを示すブール値を返します。 一般的に使用されるのは、matchsおよびlookingAtメソッドです。

matchesメソッドとlookingAtメソッドはどちらも、入力シーケンスをパターンと照合しようとします。 違いは、 matchs では入力シーケンス全体が一致する必要があるのに対し、lookupAtでは一致しないことです。

どちらの方法も、入力の先頭から始まります String

@Test
public void whenStudyMethodsWork_thenCorrect() {
    Pattern pattern = Pattern.compile("dog");
    Matcher matcher = pattern.matcher("dogs are friendly");
 
    assertTrue(matcher.lookingAt());
    assertFalse(matcher.matches());
}

次のような場合、matchesメソッドはtrueを返します。

@Test
public void whenMatchesStudyMethodWorks_thenCorrect() {
    Pattern pattern = Pattern.compile("dog");
    Matcher matcher = pattern.matcher("dog");
 
    assertTrue(matcher.matches());
}

12.3. 交換方法

置換メソッドは、入力文字列のテキストを置換するのに役立ちます。 一般的なものは、replaceFirstreplaceAllです。

replaceFirstおよびreplaceAllメソッドは、指定された正規表現に一致するテキストを置き換えます。 それらの名前が示すように、 replaceFirst は最初のオカレンスを置き換え、replaceAllはすべてのオカレンスを置き換えます。

@Test
public void whenReplaceFirstWorks_thenCorrect() {
    Pattern pattern = Pattern.compile("dog");
    Matcher matcher = pattern.matcher(
      "dogs are domestic animals, dogs are friendly");
    String newStr = matcher.replaceFirst("cat");
 
    assertEquals(
      "cats are domestic animals, dogs are friendly", newStr);
}

すべてのオカレンスを置き換えます。

@Test
public void whenReplaceAllWorks_thenCorrect() {
    Pattern pattern = Pattern.compile("dog");
    Matcher matcher = pattern.matcher(
      "dogs are domestic animals, dogs are friendly");
    String newStr = matcher.replaceAll("cat");
 
    assertEquals("cats are domestic animals, cats are friendly", newStr);
}

replaceAll メソッドを使用すると、すべての一致を同じ置換で置き換えることができます。 ケースごとに一致を置き換える場合は、トークン置換テクニックが必要になります。

13. 結論

この記事では、Javaで正規表現を使用する方法を学び、java.util.regexパッケージの最も重要な機能についても説明しました。

ここで使用されているすべてのコードサンプルを含むプロジェクトの完全なソースコードは、GitHubプロジェクトにあります。