【Java】正規表現のまとめ


一覧

パターン マッチ文字列
XYZ XYZという文字列
[XYZ] XYZいずれか一文字
[^XYZ] XYZ以外の一文字
[X-Z] X~Yの範囲で一文字
[a-zA-Z] aからz、またはA~Zの範囲
(X|Y|Z) XYZのいずれか
[ABC|]
X* Xが0回以上発生
("do*n"の場合"dn","don","dooon"など)
X+ Xが1回以上発生
("do+n"の場合"don","dooon"など)
X? Xが0または1回発生
("do?n"の場合"dn"か"don")
X{n} Xがn回発生
("do{2}n"の場合"doon")
X{n,} Xがn回以上発生
("do{2,}n"の場合"doon","doooon"など)
X{n,m} Xがn~m回発生
("do{2,3}n"の場合"doon"か"dooon")
. 任意の文字
\w 大文字/小文字の英数字、アンダースコア
[a-zA-Z_0-9]
\d 数字
[0-9]
\D 数字以外
[^0-9]
\s 空白
[ \t\n\x0B\f\r]
^ 行先頭一致
$ 行末尾一致
\b 単語境界
\\ バックスラッシュに一致
\n 改行文字に一致
\t タブ文字に一致
^\d{10}$ 半角数値10桁
^\d{5,10}$ 半角数値5桁以上10桁以下
\d{2,4}-\d{2,4}-\d{4} 電話番号
(半角数値2~3桁-半角数値2~3桁-半角数値4桁)
^\d{3}-\d{4}$ 郵便番号
(半角数値3桁-半角数値4桁)
^[0-9a-zA-Z]+$ 1桁以上の半角英数
(0-9、a-z、A-Z)

電話/郵便番号一致サンプル

//サンプル1 電話/郵便番号一致
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
 public static void main(String[] args) {
    //判定する文字列
    String str = "012-345-6789";
    //判定するパターンを生成
    Pattern p = Pattern.compile("\\d{2,4}-\\d{2,4}-\\d{4}"); //電話番号
    //Pattern p = Pattern.compile("^\\d{3}-\\d{4}$"); //郵便番号
    Matcher m = p.matcher(str);

    System.out.println(m.find()); //true
    }
}

前方/後方/部分一致サンプル

//サンプル2 前方/後方/部分一致
public class Main {
 public static void main(String[] args) {
    //判定する文字列
    String str = "000012-345-6789";
    //判定するパターンを生成
    //Pattern p = Pattern.compile("\\d{2,4}-\\d{2,4}-\\d{4}.*"); //電話番号前方一致
    //Pattern p = Pattern.compile(".*\\d{2,4}-\\d{2,4}-\\d{4}"); //電話番号後方一致
    Pattern p = Pattern.compile(".*\\d{2,4}-\\d{2,4}-\\d{4}.*"); //電話番号部分一致
    Matcher m = p.matcher(str);

    System.out.println(m.find()); //true
    }
}

マッチした文字列を抜き出す

  • PatternクラスのcompileメソッドでPatternオブジェクト準備
  • matcherメソッドで文字列検索を行うMacherオブジェクト生成
  • while (match.find())でfindメソッドがfalseになるまでマッチング繰り返し、文字列の開始位置(start)、終了位置(end)、マッチ文字列(group)を取得
    • group[0]:マッチした文字列全体
    • group[1]:サブマッチ文字列1番目の要素
import java.util.*;
import java.util.regex.Pattern;

public class Main {
  public static void main(String[] args) {
    var str = "携帯は0123-99-0000です。自宅は000-123-4567やで。";
    var ptn = Pattern.compile("(\\d{2,4})-(\\d{2,4})-(\\d{4})");
    var match = ptn.matcher(str);
    while (match.find()) {
      System.out.println("開始位置:" + match.start());
      System.out.println("終了位置:" + match.end());
      System.out.println("マッチング文字列:" + match.group());
      System.out.println("市外局番:" + match.group(1));
      System.out.println("市内局番:" + match.group(2));
      System.out.println("加入者番号:" + match.group(3));
      System.out.println("-----");
    }
  }
}

マッチング時の挙動制御

  • マッチフラグでPatternクラスをインスタンス化する時の挙動を変更できる

CASE_INSENSITIVE

  • 大文字小文字区別しない
import java.util.*;
import java.util.regex.Pattern;

public class Main {
  public static void main(String[] args) {
    var str = "仕事用は[email protected]です。プライベート用は[email protected]です。";
    var ptn = Pattern.compile("[a-z0-9.!#$%&'*+/=?^_{|}~-]+@[a-z0-9-]+(\\.[a-z0-9-]+)*", Pattern.CASE_INSENSITIVE);
    var match = ptn.matcher(str);
    while (match.find()) {
      System.out.println(match.group());
    }
  }
}

MULTILINE

  • ^$の挙動変更
  • ^は先頭のみではなく改行後の数字もマッチする
  • $の場合行末にもマッチ
import java.util.*;
import java.util.regex.Pattern;

public class Main {
  public static void main(String[] args) {
    var str = "1年生になったら友達\n100人できるかな\n";
    // var ptn = Pattern.compile("^\\d*");
    var ptn = Pattern.compile("^\\d*", Pattern.MULTILINE);
    var match = ptn.matcher(str);
    while (match.find()) {
      System.out.println(match.group()); //1 100
    }
  }
}

DOTALL

* .の挙動変更
* 改行(\n)も含んだ全部の文字列にマッチ
* 文字列を単一の行としてマッチできる(シングルラインモード)

import java.util.*;
import java.util.regex.Pattern;

public class Main {
  public static void main(String[] args) {
    var str = "会いたかった\n会いたかった\n会いたかった\nYES";
    // var ptn = Pattern.compile("^.+");
    var ptn = Pattern.compile("^.+", Pattern.DOTALL);
    var match = ptn.matcher(str);
    while (match.find()) {
      System.out.println(match.group());
      //会いたかった
      //会いたかった
      //会いたかった
      //YES
    }
  }
}

埋め込みフラグ

  • 以下コードは同じ
    • ?i :CASE_INSENSITIVE
    • ?m :MULTILINE
    • ?s :DOTALL
    • ?u :UNICODE_CASE
    • ?d :UNIX_LINES
    • ?x :COMMENTS
var ptn = Pattern.compile("(?i)[a-z0-9.!#$%&'*+/=?^_{|}~-]+@[a-z0-9-]+(\\.[a-z0-9-]+)*");

//var ptn = Pattern.compile("[a-z0-9.!#$%&'*+/=?^_{|}~-]+@[a-z0-9-]+(\\.[a-z0-9-]+)*", Pattern.CASE_INSENSITIVE);

最長一致・最短一致

  • <.+> 最長一致(出来るだけ長い文字列を一致)
    • <...>のなかに.(任意の文字)が+(1文字以上)
    • 全部のタグ文字が出力
  • <.+?> 最短一致(出来るだけ短い文字列を一致)
    • 個々のタグ文字が一個ずつ出力
import java.util.regex.Pattern;

public class Main {
  public static void main(String[] args) {
    var tags = "<p><strong>NEKO</strong>サイト<a href='index.html'><img src='cat.jpg' /></a></p>";
    //最長一致
    //var ptn = Pattern.compile("<.+>"); //<p><strong>NEKO</strong>サイト<a href='index.html'><img src='cat.jpg' /></a></p>
    //最短一致
    var ptn = Pattern.compile("<.+?>");
    var match = ptn.matcher(tags);
    while (match.find()) {
      System.out.println(match.group());
      //<p>
      //<strong>
      //</strong>
      //<a href='index.html'>
      //<img src='cat.jpg' />
      //</a>
      //</p>
    }
  }
}

名前付きキャプチャ

  • groupに名前をつけてキャプチャ
  • group("名前")でアクセス
import java.util.regex.Pattern;

public class Main {
  public static void main(String[] args) {
    var msg = "携帯は0123-99-0000です。自宅は000-123-4567やで。";
    var ptn = Pattern.compile("(?<area>\\d{2,4})-(?<city>\\d{2,4})-(?<local>\\d{4})");
    var match = ptn.matcher(msg);
    while (match.find()) {
      System.out.println("開始位置:" + match.start());
      System.out.println("終了位置:" + match.end());
      System.out.println("マッチング文字列:" + match.group());
      System.out.println("市外局番:" + match.group("area"));
      System.out.println("市内局番:" + match.group("city"));
      System.out.println("加入者番号:" + match.group("local"));
      System.out.println("-----");
    }
  }
}

後方参照

  • マッチした文字列を\\1で後から参照できる
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Main {
  //後方参照
  public static void main(String args[]){
    String str1 = "My name is <div>Neko</div>";
    String str2 = "I am a <span>Cat</span>";
    String str3 = "<span>Hello World</div>";
    String regex = "<(div|span)>.*?<\\/\\1>";
    Pattern p = Pattern.compile(regex);
    System.out.println("パターン : " + regex);
    check(p, str1);
    check(p, str2);
    check(p, str3);
  }
  private static void check(Pattern p, String target){
    Matcher m = p.matcher(target);

    if (m.find()){
      System.out.println("Match! " + target);
    }else{
      System.out.println("Unmatch! " + target);
    }
  }
}

先読み・後読み

  • 前後の文字列の有無でマッチするか判定
    • A(?=B) :Aの直後にBが続く場合のみAにマッチ
    • A(?!B) :Aの直後にBが続かない場合のみAにマッチ
    • (?<=B)A :Aの直前にBがある場合のみAにマッチ
    • (?<!B)A :Aの直前にBがない場合のみAにマッチ
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Main {

  private static void match(Pattern ptn, String input) {
    var match = ptn.matcher(input);
    while (match.find()) {
      System.out.println(match.group());
    }
    System.out.println("---");
  }

  public static void main(String[] args) {
    var re1 = Pattern.compile("わが(?=はい)"); 
    var re2 = Pattern.compile("わが(?!はい)"); 
    var re3 = Pattern.compile("(?<=。)わが");  
    var re4 = Pattern.compile("(?<!。)わが"); 
    var msg1 = "わがはいはねこである";        
    var msg2 = "わが輩はねこである。わがわがなまえはまだない。";
    match(re1, msg1); //わが
    match(re1, msg2); //---
    match(re2, msg1); //---
    match(re2, msg2); //わが,わが,わが
    match(re3, msg1); //---
    match(re3, msg2); //わが
    match(re4, msg1); //わが
    match(re4, msg2); //わが,わが
  }
}

文字列置換

  • replaceAllメソッド
  • 置き換え後の文字列に置き換え前の文字列を含めることができる
    • $0 マッチ文字列全体
    • $1 サブマッチ文字列一番目
    • ${"名前"}で名前付きチャプチャグループ使用可能
  • replaceFirstメソッド:最初の文字列のみ置換
  • * replaceメソッド:固定文字列の置換
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Main {

  public static void main(String[] args) {
    var str = "お問い合わせはこちら https://www.neko.com/です。";
    System.out.println(str.replaceAll(
        "(?i)http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\\\w ./?%&=-]*)?",
        "<a href=\"$0\">$0</a>"));
     //お問い合わせはこちら <a href="https://www.neko.com/">https://www.neko.com/</a>です。
  }
}

文字列分割

  • splitメソッド
    • cf:strigクラスのsplitは正規表現をその都度コンパイルする
    • 同じパターンを再利用する時はPatternクラスを使う
import java.util.regex.Pattern;
public class Main {
  //一桁以上数字+わ で分割
  public static void main(String[] args) {
    var str = "にわには2わうらにわには22わにわとりがいる";
    var re = Pattern.compile("\\d{1,}わ");
    var result = re.split(str);
    System.out.println(String.join(" ", result)); 
    //にわには うらにわには にわとりがいる
  }
}