Javaでの正規表現の使用

正規表現の動作について復習が必要な場合は、まず当社のインタラクティブチュートリアルをご覧ください!

Javaは、標準Javaライブラリのjava.util.regexパッケージ内のクラスを通じて正規表現をサポートしています。Java正規表現ライブラリでサポートされている高度な機能には、PCREとの間にいくつかの違いがありますが、両者は構文とパターンの大部分を共有しており、式はJavaや他の言語で使用できます。

以下の例では、コードを試行する場合は、ソースファイルの先頭に次のパッケージをインポートしてください。

import java.util.regex.*;

正規表現文字列パターン

Javaでは、通常の文字列には特殊文字(エスケープシーケンスとも呼ばれる)を含めることができます。これらはバックスラッシュ(\)で始まる文字で、改行(\n)やタブ文字(\t)などの特別なテキストを表します。そのため、Javaコードで正規表現を作成する際には、コンパイラにそれが誤ったエスケープシーケンスではないことを知らせるために、各メタ文字のバックスラッシュをエスケープする必要があります。

たとえば、パターン"There are \d dogs"を考えてみましょう。Javaでは、数字メタ文字のバックスラッシュをエスケープシーケンス\\(事実上、バックスラッシュ自体をエスケープする)を使用して、パターン"There are \\d dogs"を作成します。

これは、パターンをJavaコードにハードコーディングする場合にのみ必要です。ユーザー入力またはファイルから読み込まれた文字列は、文字ごとに個別に読み込まれ、エスケープシーケンスは解釈されません。これは、パターンをPropertiesまたはリソースファイルに配置して、読みやすく理解しやすくすることで、この問題を回避する一般的なアプローチです。

C#やPythonなどの他の言語は生の文字列の概念をサポートしていますが、Javaはまだこの便利な機能をコア言語に追加していません。

文字列の一致

Javaで正規表現を使用する際には、一般的にPatternをインスタンス化し、それをテキストと照合します。これを行う最も簡単な方法は、静的メソッドPattern.matches()を呼び出すことです。これは入力文字列と照合する正規表現を受け取り、パターンが文字列と一致するかどうかを返すだけです。

メソッド
boolean isMatch = Pattern.matches(String regex, String inputStr)

ただし、これでは、入力文字列のどこでパターンが一致するか、または一致したグループなどの追加情報は得られません。そのため、ほとんどの場合、新しいPatternをコンパイルし、それを用いて照合する各入力文字列に対して新しいMatcherを作成する方が、より有用で効率的です。これには、照合の結果が保持されます。

メソッド
Pattern ptrn = Pattern.compile(String regex) Matcher matcher = ptrn.matcher(String inputStr)
// 日付文字列に一致させる正規表現を使用してみましょう。 Pattern ptrn = Pattern.compile("([a-zA-Z]+) (\\d+)"); Matcher matcher = ptrn.matcher("June 24"); if (matcher.matches()) { // 確かに、式 "([a-zA-Z]+) (\\d+)" は日付文字列に一致します // マッチのインデックスを取得するには、Matcherオブジェクトの // start と end の値を読み取ることができます。 // これは [0, 7] を出力します。これは文字列の先頭と末尾で一致するためです System.out.println("Match at index [" + matcher.start() + ", " + matcher.end() + ")"); // 完全一致したテキストを取得するには、Matcherオブジェクトの group を読み取ることができます // これは "June 24" を出力します System.out.println("Match: " + matcher.group()); }

キャプチャグループ

正規表現でのキャプチャグループは、上記の例のように文字列と一致させるのと同じくらい簡単です。Patternを使用して入力文字列と一致させた後、返されたMatcher内の抽出されたグループを反復処理するだけです。

// いくつかの日付文字列からデータをキャプチャする正規表現を使用してみましょう。 String pattern = "([a-zA-Z]+) (\\d+)"; Pattern ptrn = Pattern.compile("([a-zA-Z]+) (\\d+)"); Matcher matcher = ptrn.matcher("June 24, August 9, Dec 12"); // これは、各マッチと入力文字列内のマッチが見つかったインデックスを出力します // June 24 at index [0, 7) // August 9 at index [9, 17) // Dec 12 at index [19, 25) while (matcher.find()) { System.out.println(String.format("Match: %s at index [%d, %d]", matcher.group(), matcher.start(), matcher.end())); } // グループをマッチ内で再度反復処理する場合は、まず // マッチャーを入力文字列の先頭から開始するようにリセットします。 matcher.reset(); // 各マッチについて、キャプチャされたグループを読み取ることで、 // キャプチャされた情報を抽出できます。 while (matcher.find()) { // これは、このマッチ内のキャプチャされたグループの数を表示します System.out.println(String.format("%d groups captured", matcher.groupCount())); // これは、各マッチの月と日を表示します。 // 最初のグループは常に一致したテキスト全体であるため、月は // インデックス 1 から始まります。 System.out.println("Month: " + matcher.group(1) + ", Day: " + matcher.group(2)); // マッチ内の各グループにも開始インデックスと終了インデックスがあり、 // これはグループが見つかった入力文字列内のインデックスです。 System.out.println(String.format("Month found at[%d, %d)", matcher.start(1), matcher.end(1))); }

文字列の検索と置換

もう1つの一般的なタスクは、正規表現を使用して文字列の一部を検索して置換することです。たとえば、古いメールドメインのすべてのインスタンスを置換したり、テキストの順序を入れ替えたりする場合などです。Matcher.replaceAll()メソッドとMatcher.replaceFirst()メソッドを使用して、Javaでこれを実行できます。これらのメソッドはどちらも、最初にマッチャーを、入力文字列の先頭から文字列の末尾まで、または最初のマッチの末尾までそれぞれリセットします。

置換文字列には、パターン内のキャプチャされたグループへの参照(ドル記号_$_を使用)または通常のリテラル文字列を含めることができます。

メソッド
String replacedString = matcher.replaceAll(String inputStr);
String replacedString = matcher.replaceFirst(String inputStr);
// 日付文字列において、日と月の順番を入れ替えてみましょう。// 置換文字列にもメタ文字(キャプチャグループへの逆参照)が含まれているため、// これもverbatim文字列を使用します。

Pattern フラグ

Patternをコンパイルする際に、入力文字列の一致方法を変更する追加のフラグを渡すことができます。利用可能なフラグのほとんどは便宜的なもので、正規表現自体に直接記述できますが、特定のケースでは役立つものもあります。

  • Pattern.CASE_INSENSITIVE は、パターンを大文字と小文字を区別しないようにし、異なる大文字小文字の文字列と一致させます。
  • Pattern.MULTILINE は、入力文字列に改行文字(\n)がある場合に必要です。開始および終了メタ文字(それぞれ^および$)が、入力文字列全体の先頭と末尾ではなく、各行の先頭と末尾と一致することを許可します。
  • Pattern.DOTALL は、ドットメタ文字(.)が改行文字にも一致することを許可します。
  • Pattern.LITERAL は、パターンをリテラルにします。つまり、エスケープされた文字はそのまま一致します。たとえば、パターン"\d"は、数字文字ではなく、バックスラッシュの後に'd'文字が続くものと一致します。

リンク

Javaでの正規表現の使用に関する詳細については、次のリンクをご覧ください。