直前の文字を繰り返す文字列にマッチするパターンを記述する

正規表現では直前の文字が指定した回数繰り返される文字列にマッチするパターンを定義することもできます。回数は 1 回以上や 0 回以上、または繰り返す回数を指定することもできます。繰り返す対象は文字の他に文字クラスなどを使ったパターンも可能です。ここでは Java の正規表現で文字や他のパターンを指定した回数繰り返す文字列にマッチするパターンを記述する方法について解説します。

(Last modified: )

直前の文字が0回以上連続する文字にマッチする(*)

メタ文字のひとつであるアスタリスク(*)は直前の文字が 0 回以上連続する文字列にマッチします( 0 回以上というのは一度もなくてもいいし何回出てもいいという意味です)。

*    最長一致数量子
*?   最短一致数量子
*+   強欲な数量子

※ 最長一致数量子などについては「最長一致数量子、最短一致数量子、強欲な数量子の違い」を参照されてください。

例えば次のようなパターンは最初に A 、続いて B の文字が 0 回以上続き、そのあとに C が続く文字列にマッチします。

String regex = "AB*C";

AC や ABC の他、 ABBBC や ABBBBBBBBBC などにもマッチします。

ACABCABBBBC
✕ ADC
✕ ABEC

実際にサンプルで試してみます。

import java.util.regex.*;

class JSample10_1{
  public static void main(String[] args){
    String regex = "Ha*n";
    Pattern p = Pattern.compile(regex);

    Matcher m1 = p.matcher("Hn");
    System.out.println(m1.find());  // true

    Matcher m2 = p.matcher("Han");
    System.out.println(m2.find());  // true

    Matcher m3 = p.matcher("Haaaaaan");
    System.out.println(m3.find());  // true

    Matcher m4 = p.matcher("HaaYaan");
    System.out.println(m4.find());  // false
  }
}

直前の文字が1回以上連続する文字にマッチする(+)

メタ文字のひとつであるプラス(+)は直前の文字が 1 回以上連続する文字列にマッチします。

+    最長一致数量子
+?   最短一致数量子
++   強欲な数量子

例えば次のようなパターンは最初に A 、続いて B の文字が 1 回以上続き、そのあとに C が続く文字列にマッチします。

String regex = "AB+C";

ABC や ABBBC の他に ABBBBBBBBBC などにもマッチします。

ABCABBBBC
✕ AC
✕ ADC
✕ ABEC

実際にサンプルで試してみます。

import java.util.regex.*;

class JSample10_2{
  public static void main(String[] args){
    String regex = "Ha+n";
    Pattern p = Pattern.compile(regex);

    Matcher m1 = p.matcher("Hn");
    System.out.println(m1.find());  // false

    Matcher m2 = p.matcher("Han");
    System.out.println(m2.find());  // true

    Matcher m3 = p.matcher("Haaaaaan");
    System.out.println(m3.find());  // true

    Matcher m4 = p.matcher("HaaYaan");
    System.out.println(m4.find());  // false
  }
}

直前の文字が0回または1回現れる文字にマッチする(?)

メタ文字のひとつであるクエスチョンマーク(?)は直前の文字が 0 回または 1 回現れる文字にマッチします。

?    最長一致数量子
??   最短一致数量子
?+   強欲な数量子

例えば次のようなパターンは最初に A 、続いて B の文字が 0 回か 1 回続き、そのあとに C が続く文字列にマッチします。

String regex = "AB?C";

AC または ABC とマッチします。

ACABC
✕ ABBC
✕ ADC

実際にサンプルで試してみます。

import java.util.regex.*;

class JSample10_3{
  public static void main(String[] args){
    String regex = "Ha?n";
    Pattern p = Pattern.compile(regex);

    Matcher m1 = p.matcher("Hn");
    System.out.println(m1.find());  // true

    Matcher m2 = p.matcher("Han");
    System.out.println(m2.find());  // true

    Matcher m3 = p.matcher("Haaaaaan");
    System.out.println(m3.find());  // false

    Matcher m4 = p.matcher("Hon");
    System.out.println(m4.find());  // false
  }
}

直前の文字をnum回繰り返す文字にマッチする({num})

メタ文字のひとつである {num} は直前の文字を num 回繰り返す文字列にマッチします。

{num}    最長一致数量子
{num}?   最短一致数量子
{num}+   強欲な数量子

例えば次のようなパターンは最初に A 、続いて B の文字が 3 回続き、そのあとに C が続く文字列にマッチします。

String regex = "AB{3}C";

ABBBC とマッチします。

ABBBC
✕ AC
✕ ABBC
✕ ABBBBC
✕ ADC

実際にサンプルで試してみます。

import java.util.regex.*;

class JSample10_4{
  public static void main(String[] args){
    String regex = "Ha{2}n";
    Pattern p = Pattern.compile(regex);

    Matcher m1 = p.matcher("Hn");
    System.out.println(m1.find());  // false

    Matcher m2 = p.matcher("Han");
    System.out.println(m2.find());  // false

    Matcher m3 = p.matcher("Haan");
    System.out.println(m3.find());  // true

    Matcher m4 = p.matcher("Haaan");
    System.out.println(m4.find());  // false
  }
}

直前の文字をmin回以上max回以下繰り返す文字にマッチする({min,max})

メタ文字のひとつである {min,max} は直前の文字を min 回以上 max 回以下繰り返す文字列にマッチします。

{min,max}    最長一致数量子
{min,max}?   最短一致数量子
{min,max}+   強欲な数量子

max を省略して記述することもできます。

{min,}    最長一致数量子
{min,}?   最短一致数量子
{min,}+   強欲な数量子

※ {2,4} ではなく {2, 4} のように max の値の前に半角スペースを入れると、実行時に「Exception in thread "main" java.util.regex.PatternSyntaxException: Unclosed counted closure」というエラーが発生します。

※ 他のプログラミング言語では min を省略した {,max} の書式を使用できるものもありますが、 Java では実行時に「Exception in thread "main" java.util.regex.PatternSyntaxException: Illegal repetition」というエラーが発生します。

例えば次のようなパターンは最初に A 、続いて B の文字が 2 回以上 4 回以下繰り返し、そのあとに C が続く文字列にマッチします。

String regex = "AB{2,4}C";

ABBC 、 ABBBC 、 ABBBBC とマッチします。

ABBCABBBCABBBBC
✕ AC
✕ ABC
✕ ABBBBBC

例えば次のような max を省略したパターンは最初に A 、続いて B の文字が 2 回以上繰り返し、そのあとに C が続く文字列にマッチします。

String regex = "AB{2,}C";

ABBC 、 ABBBC 、 ABBBBBBBC などとマッチします。

ABBCABBBCABBBBBBBC
✕ AC
✕ ABC

実際にサンプルで試してみます。

import java.util.regex.*;

class JSample10_5{
  public static void main(String[] args){
    String regex = "Ha{2,3}n";
    Pattern p = Pattern.compile(regex);

    Matcher m1 = p.matcher("Hn");
    System.out.println(m1.find());  // false

    Matcher m2 = p.matcher("Han");
    System.out.println(m2.find());  // false

    Matcher m3 = p.matcher("Haan");
    System.out.println(m3.find());  // true

    Matcher m4 = p.matcher("Haaan");
    System.out.println(m4.find());  // true

    Matcher m5 = p.matcher("Haaaan");
    System.out.println(m5.find());  // false
  }
}

文字ではなく直前のパターンを繰り返す

ここまで解説した繰り返しを行うメタ文字は、繰り返しの対象が直前の文字だけでなく直前の別のパターンを繰り返す文字列にもマッチします。例えば任意の文字を表すドット(.)の直後にアスタリスク(*)を記述すると、任意の文字を 0 回以上繰り返す文字列にマッチします。

.*

例えば次のようなパターンは最初に A が現れ、そのあとに任意の文字が 0 回以上続き、最後に B が現れる文字列にマッチします。

String regex = "A.*B";

また 0 から 9 までのいずれかの文字とマッチする文字クラスの [0-9] の直後に {4} を記述すると、任意の数字が 4 回続く文字列にマッチします。

[0-9]{4}

例えば次のようなパターンは最初に任意の数字が 3 回続き、そのあとにハイフン(-)が続き、最後に任意の数字が 4 回続く文字列にマッチします。例えば 123-4567 のような文字列です。

String regex = "[0-9]{3}-[0-9]{4}";

実際にサンプルで試してみます。

import java.util.regex.*;

class JSample10_6{
  public static void main(String[] args){
    String regex1 = "<em>.*</em>";
    Pattern p1 = Pattern.compile(regex1);

    Matcher m1 = p1.matcher("<p>今日は<em>快晴の</em>一日です</p>");
    if (m1.find()){
      System.out.println(m1.group());  // <em>快晴の</em>
    }

    String regex2 = "[0-9]{3}-[0-9]{4}";
    Pattern p2 = Pattern.compile(regex2);

    Matcher m2 = p2.matcher("郵便番号は〒123-4567です");
    if (m2.find()){
      System.out.println(m2.group());  // 123-4567
    }
  }
}

最初のパターンでは <em> のあとに任意の文字が 0 文字以続き、そのあとで </em> が続く文字列とマッチします。次のパターンでは数値が 3 つ続いたあと、ハイフン(-)が続き、そのあとで数値が 4 つ続く文字列とマッチします。

グループ化したパターンを繰り返す

ここまでは繰り返しの対象が直前の文字やパターンの場合でしたが、複数の文字やパターンを括弧()で囲んでグループ化することで、グループ化した複数の文字やパターンを繰り返す文字列にもマッチさせることができます。

例えば数値が 1 つから 3 つ続いたあとにドット(.)が続く文字列を 3 回繰り返すパターンを作成してみます。最初に数値が 1 つから 3 つ続いたあとにドット(.)が続く文字列のパターンは次のように記述できます(文字としてのドット(.)を使用するのでバックスラッシュ(\)を使って \. のようにエスケープしています)。

String regex = "[0-9]{1,3}\\.";

このパターン全体を 3 回繰り返すパターンなので、単に {3} を付け加えるのではなく、対象のパターン全体を括弧()で囲んでグループ化したあとで {3} を記述します。

String regex = "([0-9]{1,3}\\.){3}";

これで数値が 1 つから 3 つ続いたあとにドット(.)が続く文字列を 3 回繰り返すパターンが完成です。

実際にサンプルで試してみます。

import java.util.regex.*;

class JSample10_7{
  public static void main(String[] args){
    String regex = "([0-9]{1,3}\\.){3}[0-9]{1,3}";
    Pattern p = Pattern.compile(regex);

    Matcher m = p.matcher("IPアドレスは192.168.0.18です");
    if (m.find()){
      System.out.println(m.group());  // 192.168.0.18
    }
  }
}

今回のパターンは数値が 1 から 3 個続き、そのあとに文字のドット(.)が続く組み合わせが 3 回続いたあと、最後に数値が 1 から 3 個続く文字列とマッチします。少しややこしいですが "([0-9]{1,3}\\.){3}" のあとに "[0-9]{1,3}" が続いています。

-- --

Java の正規表現で文字や他のパターンを指定した回数繰り返す文字列にマッチするパターンを記述する方法について解説しました。

( Written by Tatsuo Ikura )

Profile
profile_img

著者 / TATSUO IKURA

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