名前付き正規表現グループの使い方

正規表現のパターンに複数の正規表現グループを設定した場合、インデックスで正規表現グループを区別しますが、あらかじめ正規表現グループに名前を付けておくとインデックスの代わりに名前を使って正規表現グループを区別することができます。マッチした文字列を取得する場合にインデックスではなくグループ名を指定して取得することができます。ここでは Java の正規表現で名前付き正規表現グループを利用する方法について解説します。

(Last modified: )

名前付き正規表現グループを設定する

パターンの中に複数の正規表現グループを設定した場合、正規表現グループのインデックスを指定してマッチした文字列を取得しますが、正規表現グループに名前を付けておき、名前を指定してマッチした文字列を取得することもできます。

名前付きの正規表現グループを設定するには括弧の中の最初に ?<グループ名> を記述します。

(?<グループ名>パターン)

グループ名は A から Z 、 a から z 、 0 から 9 までの文字の組み合わせで指定します。最初の文字は英文字である必要があります。

次のサンプルを見てください。パターンの中に 2 つの正規表現グループが設定されていますが、最初の正規表現グループの名前を cate 、次の正規表現グループの名前を code に設定しています。

String regex = "(?<cate>[A-Z]{2})-(?<code>\\d{2})";

名前付き正規表現グループでマッチした文字列を取得する

名前付きの正規表現グループを設定した場合、あとから正規表現グループ毎にマッチした文字列を参照するには Matcher クラスの group メソッドを使用します。

public String group(String name)

パラメータ:
name - この正規表現エンジンのパターンに指定されている名前付きの前方参照を行う正規表現グループの名前

戻り値:
前回のマッチ時にグループによって名前付きの前方参照された部分シーケンス(空の場合もある)。グループが入力の一部へのマッチに失敗した場合はnull

例外:
IllegalStateException - マッチがまだ試みられていない場合、または前回のマッチ操作が失敗した場合
IllegalArgumentException - 指定された名前を持つ前方参照を行う正規表現グループがそのパターンに含まれない場合

1 番目の引数に正規表現グループのグループ名を指定します。戻り値として指定したグループ名の正規表現グループでマッチした文字列が返されます。

次のサンプルを見てください。

// java.util.regex.*のインポートが必要です
import java.util.regex.*;

String regex = "(?<year>[0-9]{4})/(?<month>[0-9]{2})";
Pattern p = Pattern.compile(regex);

Matcher m = p.matcher("1992/10");
if (m.find()){
  System.out.println(m.group("year"));  // 1992
  System.out.println(m.group("month"));  // 10
}

パターンに名前付き正規表現グループを設定しておき、 find メソッドなどでパターンが対象の文字列にマッチした場合にグループ名を指定して正規表現グループとマッチした文字列を取得しています。

サンプルコード

それでは簡単なサンプルプログラムを作って試してみます。テキストエディタで次のように記述したあと、 JSample14-1.java という名前で保存します。

import java.util.regex.*;

class JSample14_1{
  public static void main(String[] args){
    String regex = "(?<pref>^.*(?:都|道|府|県))(?<ward>.*区)";
    Pattern p = Pattern.compile(regex);

    Matcher m = p.matcher("東京都港区赤坂");
    if (m.find()){
      System.out.println(m.group());
      System.out.println(m.group("pref"));
      System.out.println(m.group("ward"));
    }
  }
}

コンパイルを行います。

javac -encoding UTF-8 JSample14_1.java

その後で、次のように実行してください。

java JSample14_1

名前付き正規表現グループとマッチした文字列を取得する(1)

名前付き正規表現グループにマッチした文字列に対して、それぞれグループの名前を指定して取得しました。

名前付き正規表現グループでマッチした文字列の最初の文字と最後の文字のインデックスを取得する

名前付き正規表現グループとマッチした文字列の最初の文字のインデックスと最後の文字のインデックスを取得することができます。インデックスの取得を行うには Matcher クラスの start メソッドと end メソッドを使用します。

public int start(String name)

パラメータ:
name - この正規表現エンジンのパターンに指定されている名前付きの前方参照を行う正規表現グループの名前

戻り値:
グループによって前方参照された最初の文字のインデックス。マッチは成功したがグループ自体はどの部分にもマッチしなかった場合は-1

例外:
IllegalStateException - マッチがまだ試みられていない場合、または前回のマッチ操作が失敗した場合
IllegalArgumentException - 指定された名前を持つ前方参照を行う正規表現グループがそのパターンに含まれない場合

public int end(String name)

パラメータ:
name - この正規表現エンジンのパターンに指定されている名前付きの前方参照を行う正規表現グループの名前

戻り値:
グループによって前方参照された最後の文字の後のオフセット。マッチは成功したがグループ自体はどの部分にもマッチしなかった場合は-1

例外:
IllegalStateException - マッチがまだ試みられていない場合、または前回のマッチ操作が失敗した場合
IllegalArgumentException - 指定された名前を持つ前方参照を行う正規表現グループがそのパターンに含まれない場合

どちらのメソッドも 1 番目の引数にグループの名前を指定します。戻り値として指定した名前の正規表現グループでマッチした文字列が最初の文字のインデックス、および最後の文字の次の文字のインデックスが返ります。

次のサンプルを見てください。

// java.util.regex.*のインポートが必要です
import java.util.regex.*;

String regex = "(?<year>[0-9]{4})/(?<month>[0-9]{2})";
Pattern p = Pattern.compile(regex);

Matcher m = p.matcher("1992/10");
if (m.find()){
  System.out.println(m.group("year"));  // 1992
  System.out.println(m.start("year"));  // 0
  System.out.println(m.end("year"));    // 4
}

パターンに名前付き正規表現グループを設定しておき、 find メソッドなどでパターンが対象の文字列にマッチした場合にグループ名を指定して正規表現グループとマッチした文字列の最初の文字のインデックスと最後の文字の次の文字のインデックスを取得しています。

サンプルコード

それでは簡単なサンプルプログラムを作って試してみます。テキストエディタで次のように記述したあと、 JSample14-2.java という名前で保存します。

import java.util.regex.*;

class JSample14_2{
  public static void main(String[] args){
    String regex = "(?^.*(?:都|道|府|県))(?.*区)";
    Pattern p = Pattern.compile(regex);

    Matcher m = p.matcher("東京都港区赤坂");
    if (m.find()){
      System.out.println(m.group());
      System.out.println(m.group("pref"));
      System.out.println(m.start("pref"));
      System.out.println(m.end("pref"));
      System.out.println(m.group("ward"));
      System.out.println(m.start("ward"));
      System.out.println(m.end("ward"));
    }
  }
}

コンパイルを行います。

javac -encoding UTF-8 JSample14_2.java

その後で、次のように実行してください。

java JSample14_2

名前付き正規表現グループでマッチした文字列の最初の文字と最後の文字のインデックスを取得する(1)

名前付き正規表現グループにマッチした文字列および文字列の最初の文字のインデックスと最後の文字の次の文字のインデックスに対して、それぞれグループの名前を指定して取得しました。

名前付き正規表現グループでキャプチャを後方参照しあとから同じ値とマッチする

パターンの中で正規表現グループを設定した場合、正規表現グループでマッチした値を同じパターン内から後方参照し、既にマッチした値と同じ値にマッチするようにパターンに記述することができます。

名前付き正規表現グループの場合、参照する場合グループ名を使っては \k<グループ名> のようにパターン内に記述します。

String regex = "(?<alpha>ABC).*\\k<alpha>";

この場合、 ABC から始まり、任意の文字が 0 回以上続いたあと、 1 番目のキャプチャグループでキャプチャした値と同じ値が続くパターンとなります。 1 番目のキャプチャグループでキャプチャした値は ABC なので /ABC.*ABC/ というパターンと同じです。

サンプルコード

それでは簡単なサンプルプログラムを作って試してみます。テキストエディタで次のように記述したあと、 JSample14-3.java という名前で保存します。

import java.util.regex.*;

class JSample14_3{
  public static void main(String[] args){
    String regex = "<(?<tag>.+)>(.*)</\\k<tag>>";
    Pattern p = Pattern.compile(regex);

    Matcher m = p.matcher("AAA<div>BBB<span>CCC</span>DDD</div>EEE");
    if (m.find()){
      System.out.println(m.group(2));  // BBB<span>CCC</span>DDD
    }
  }
}

コンパイルを行います。

javac -encoding UTF-8 JSample14_3.java

その後で、次のように実行してください。

java JSample14_3

名前付き正規表現グループでキャプチャを後方参照しあとから同じ値とマッチする(1)

今回のサンプルでは最初に見つかったタグ(今回の場合は <div> )と同じ名前の閉じタグ(今回の場合は </div> )で囲まれた文字列にマッチしています。同じ名前の閉じタグを探すためにキャプチャした値を後から参照しています。

-- --

Java の正規表現で名前付き正規表現グループを利用する方法について解説しました。

( Written by Tatsuo Ikura )

Profile
profile_img

著者 / TATSUO IKURA

プログラミングや開発環境構築の解説サイトを運営しています。