1. 概要

非キャプチャグループは、Java正規表現内の重要な構成要素です。 これらは、単一のユニットとして機能するサブパターンを作成しますが、一致した文字シーケンスは保存しません。 このチュートリアルでは、Java正規表現で非キャプチャグループを使用する方法について説明します。

2. 正規表現グループ

正規表現グループは、キャプチャと非キャプチャの2つのタイプのいずれかになります。

グループをキャプチャすると、一致した文字シーケンスが保存されます。 それらの値は、パターンの後方参照として使用したり、コードの後半で取得したりできます。

一致した文字シーケンスは保存されませんが、 非キャプチャグループは、グループ内のパターンマッチング修飾子を変更できます。 一部の非キャプチャグループは、サブパターンマッチが成功した後、バックトラッキング情報を破棄することもできます。

キャプチャされていないグループの実際の例をいくつか見てみましょう。

3. 非キャプチャグループ

非キャプチャグループは、演算子(?: X)“で作成されます。 「X」は、グループのパターンです。

Pattern.compile("[^:]+://(?:[.a-z]+/?)+")

このパターンには、単一の非キャプチャグループがあります。 URLに似ている場合は、値と一致します。 URLの完全な正規表現は、はるかに複雑になります。 キャプチャしないグループに焦点を当てるために、単純なパターンを使用しています。

パターン 「[^:]:」はプロトコルと一致します—たとえば、「 http://」。 非キャプチャグループ「(?:[。az] + /?)」は、ドメイン名をオプションのスラッシュと一致させます。 「+」演算子はこのパターンの1つ以上のオカレンスに一致するため、後続のパスセグメントにも一致します。 このパターンをURLでテストしてみましょう。

Pattern simpleUrlPattern = Pattern.compile("[^:]+://(?:[.a-z]+/?)+");
Matcher urlMatcher
  = simpleUrlPattern.matcher("http://www.microsoft.com/some/other/url/path");
    
Assertions.assertThat(urlMatcher.matches()).isTrue();

一致したテキストを取得しようとするとどうなるか見てみましょう。

Pattern simpleUrlPattern = Pattern.compile("[^:]+://(?:[.a-z]+/?)+");
Matcher urlMatcher = simpleUrlPattern.matcher("http://www.microsoft.com/");
    
Assertions.assertThat(urlMatcher.matches()).isTrue();
Assertions.assertThatThrownBy(() -> urlMatcher.group(1))
  .isInstanceOf(IndexOutOfBoundsException.class);

正規表現は、java.util.Patternオブジェクトにコンパイルされます。 次に、 java.util.Matcher を作成して、Patternを指定された値に適用します。

次に、 matches()の結果がtrueを返すことを表明します。

URLのドメイン名と一致させるために、非キャプチャグループを使用しました。 非キャプチャグループは一致したテキストを保存しないため、一致したテキスト「www.microsoft.com/」を取得できません。ドメイン名を取得しようとすると、IndexOutOfBoundsExceptionが発生します。

3.1. インライン修飾子

正規表現では大文字と小文字が区別されます。パターンを大文字と小文字が混在するURLに適用すると、一致は失敗します。

Pattern simpleUrlPattern
  = Pattern.compile("[^:]+://(?:[.a-z]+/?)+");
Matcher urlMatcher
  = simpleUrlPattern.matcher("http://www.Microsoft.com/");
    
Assertions.assertThat(urlMatcher.matches()).isFalse();

大文字も一致させたい場合は、いくつかのオプションを試すことができます。

1つのオプションは、大文字の範囲をパターンに追加することです。

Pattern.compile("[^:]+://(?:[.a-zA-Z]+/?)+")

もう1つのオプションは、修飾子フラグを使用することです。 したがって、大文字と小文字を区別しないように正規表現をコンパイルできます。

Pattern.compile("[^:]+://(?:[.a-z]+/?)+", Pattern.CASE_INSENSITIVE)

非キャプチャグループでは、3番目のオプションが可能です。グループのみの修飾子フラグを変更できます。大文字と小文字を区別しない修飾子フラグ( “ i “)をグループに追加しましょう。

Pattern.compile("[^:]+://(?i:[.a-z]+/?)+");

グループで大文字と小文字を区別しないようにしたので、このパターンを大文字と小文字が混在するURLに適用してみましょう。

Pattern scopedCaseInsensitiveUrlPattern
  = Pattern.compile("[^:]+://(?i:[.a-z]+/?)+");
Matcher urlMatcher
  = scopedCaseInsensitiveUrlPattern.matcher("http://www.Microsoft.com/");
    
Assertions.assertThat(urlMatcher.matches()).isTrue();

大文字と小文字を区別しないようにパターンをコンパイルする場合、修飾子の前に「-」演算子を追加することでオフにできます。このパターンを別の大文字と小文字が混在するURLに適用してみましょう。

Pattern scopedCaseSensitiveUrlPattern
  = Pattern.compile("[^:]+://(?-i:[.a-z]+/?)+/ending-path", Pattern.CASE_INSENSITIVE);
Matcher urlMatcher
  = scopedCaseSensitiveUrlPattern.matcher("http://www.Microsoft.com/ending-path");
  
Assertions.assertThat(urlMatcher.matches()).isFalse();

この例では、最終パスセグメント「/ending-path」では大文字と小文字が区別されません。 パターンの「/ending-path」の部分は大文字と小文字に一致します。

グループ内で大文字と小文字を区別しないオプションをオフにした場合、非キャプチャグループは小文字のみをサポートしていました。 したがって、大文字と小文字が混在するドメイン名が一致しませんでした。

4. 独立した非キャプチャグループ

独立した非キャプチャグループは、正規表現グループの一種です。 これらのグループは、一致が成功したを見つけた後、バックトラッキング情報を破棄します。 このタイプのグループを使用する場合、バックトラックがいつ発生する可能性があるかを認識する必要があります。 そうしないと、パターンが必要と思われる値と一致しない可能性があります。

バックトラッキングは、非決定性有限オートマトン(NFA)正規表現エンジンの機能です。 エンジンがテキストと一致しない場合、NFAエンジンはパターン内の代替を探索できます。エンジンは、使用可能なすべての代替を使い果たした後、一致に失敗します。 独立した非キャプチャグループに関連するバックトラックについてのみ説明します。

独立した非キャプチャグループは、演算子「(?> X)」で作成されます。ここで、Xはサブパターンです。

Pattern.compile("[^:]+://(?>[.a-z]+/?)+/ending-path");

定数パスセグメントとして「/ending-path」を追加しました。 この追加要件があると、バックトラック状況が発生します。 ドメイン名およびその他のパスセグメントは、スラッシュ文字と一致する場合があります。 「/ending-path」と一致させるには、エンジンをバックトラックする必要があります。 バックトラックすることで、エンジンはグループからスラッシュを削除し、パターンの「/ending-path」部分に適用できます。

独立した非キャプチャグループパターンをURLに適用してみましょう。

Pattern independentUrlPattern
  = Pattern.compile("[^:]+://(?>[.a-z]+/?)+/ending-path");
Matcher independentMatcher
  = independentUrlPattern.matcher("http://www.microsoft.com/ending-path");
    
Assertions.assertThat(independentMatcher.matches()).isFalse();

グループはドメイン名とスラッシュに正常に一致します。 したがって、独立した非キャプチャグループのスコープを残します。

このパターンでは、「ending-path」の前にスラッシュを表示する必要があります。 ただし、独立した非キャプチャグループはスラッシュと一致しています。

NFAエンジンはバックトラックを試行する必要があります。 グループの最後ではスラッシュはオプションであるため、NFAエンジンはグループからスラッシュを削除して、再試行します。 独立した非キャプチャグループは、バックトラッキング情報を破棄しました。 したがって、NFAエンジンはバックトラックできません。

4.1. グループ内のバックトラック

バックトラッキングは、独立した非キャプチャグループ内で発生する可能性があります。 NFAエンジンがグループと一致している間、バックトラッキング情報は破棄されていません。 グループが正常に一致するまで、バックトラッキング情報は破棄されません。

Pattern independentUrlPatternWithBacktracking
  = Pattern.compile("[^:]+://(?>(?:[.a-z]+/?)+/)ending-path");
Matcher independentMatcher
  = independentUrlPatternWithBacktracking.matcher("http://www.microsoft.com/ending-path");
    
Assertions.assertThat(independentMatcher.matches()).isTrue();

これで、独立した非キャプチャグループ内に非キャプチャグループができました。 「ending-path」の前にスラッシュが含まれるバックトラック状況がまだあります。 ただし、パターンのバックトラッキング部分は、独立した非キャプチャグループ内に含まれています。 バックトラックは、独立した非キャプチャグループ内で発生します。 したがって、NFAエンジンにはバックトラックするのに十分な情報があり、パターンは提供されたURLと一致します。

5. 結論

非キャプチャグループはキャプチャグループとは異なることを示しました。 ただし、それらは、対応するキャプチャのように単一のユニットとして機能します。 また、非キャプチャグループは、パターン全体ではなく、グループの修飾子を有効または無効にできることも示しました

同様に、独立した非キャプチャグループがバックトラッキング情報を破棄する方法を示しました。 この情報がないと、NFAエンジンは、一致を成功させるための代替案を探すことができません。ただし、グループ内でバックトラックが発生する可能性があります。

いつものように、ソースコードはGitHubから入手できます。