【Java】正規表現のまとめ

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

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

```java
//サンプル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
    }
}
```
###前方/後方/部分一致サンプル

```java
//サンプル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番目の要素

```java
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
* 大文字小文字区別しない

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

public class Main {
  public static void main(String[] args) {
    var str = "仕事用はneko@example.comです。プライベート用はNEKO@example.comです。";
    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
* `^`、`$`の挙動変更
* `^`は先頭のみではなく改行後の数字もマッチする
* `$`の場合行末にもマッチ

```java
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`)も含んだ全部の文字列にマッチ
*　文字列を単一の行としてマッチできる（シングルラインモード）

```java
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

```java
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文字以上)
    * 全部のタグ文字が出力
* **<.+?>** 最短一致(出来るだけ短い文字列を一致)
    * 個々のタグ文字が一個ずつ出力


```java
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("名前")`でアクセス

```java
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`で後から参照できる

```java
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にマッチ

```java
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メソッド**：固定文字列の置換

```java
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クラスを使う

```java
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)); 
    //にわには うらにわには にわとりがいる
  }
}
```