S-JIS[2009-02-02/2019-12-08] 変更履歴
Javaで正規表現を扱うには、java.util.regex.Patternクラス(Matcherクラス)を使用する。(JDK1.4以降)
正規表現でどのような文字が使えるのか(正規表現構文)はPatternのJavadocに載っている。
文字列がパターン全体に完全にマッチするかどうかを判定する。
import java.util.regex.Pattern;
String regexp = "ab.*ef"; String test = "abcdef"; boolean match = Pattern.matches(regexp, test); System.out.println(match);
↓実行結果
true
「.
」はどんな文字でもマッチする(1文字分。ただしデフォルトでは改行コードは含まない)。
「*」は直前の文字が0個以上という意味。
したがって「.
*」は、どんな文字列にもマッチする。
それ以外のアルファベットはその文字そのもの。
したがって「ab.
*ef」は、abで始まってefで終わる文字列にマッチする。
matches()メソッドは、文字列が正規表現にマッチした場合にtrueになる。
→内部ではMatcherクラスを使っている
文字列をパターンによって分割する。[2014-04-15]
String#split()は、内部ではPattern#split()が使われている。
Pattern pattern = Pattern.compile(":"); String[] ss = pattern.split("a:bc:def", 0); //「:」で分割 for(String s:ss){ System.out.println(s); }
↓実行結果
a bc def
split()の第2引数limitは、何個に分割するかを指定する。
1を指定すると1個になる(つまり分割されない)。2を指定すると二分割。
0を指定すると、無制限に分割される。
第2引数の無いsplit()は、limitに0を指定したものと同じ。
split()と同様に文字列を分割するが、Stream<String>を返す。(JDK1.8)[2014-04-15]
import java.util.regex.Pattern; import java.util.stream.Stream;
Pattern pattern = Pattern.compile(":"); Stream<String> stream = pattern.splitAsStream("a:bc:def"); stream.forEach(System.out::println);
↓実行結果
a bc def
「正規表現で特別な意味を持つ文字」をエスケープしたパターン文字列を返す。(JDK1.5)[2014-04-15]
エスケープ無し | 手動エスケープ | quoteメソッド使用 | |
---|---|---|---|
例 |
String[] ss = "a.b.c".split("."); System.out.println(Arrays.toString(ss)); |
String[] ss = "a.b.c".split("\\."); System.out.println(Arrays.toString(ss)); |
String[] ss = "a.b.c".split(Pattern.quote(".")); System.out.println(Arrays.toString(ss)); |
実行結果 | [] |
[a, b, c] |
[a, b, c] |
パターンにマッチするものが1つでもあるかどうかを判定する関数を返す。(JDK1.8)[2014-04-15]
Pattern pattern = Pattern.compile("123"); Predicate<String> predicate = pattern.asPredicate(); System.out.println(predicate.test("abc123def"));
↓実行結果
true
これは、以下の関数(ラムダ式)と同じ。
Pattern pattern = Pattern.compile("123"); Predicate<String> predicate = s -> pattern.matcher(s).find();
パターンに(完全に)マッチするかどうかを判定する関数を返す。(Java11)[2018-10-01]
var pattern = Pattern.compile("abc"); var predicate = pattern.asMatchPredicate(); System.out.println(predicate.test("abc"));
↓実行結果
true
これは、以下の関数(ラムダ式)と同じ。
var pattern = Pattern.compile("123"); Predicate<String> predicate = s -> pattern.matcher(s).matches();
Pattern#matches()は、内部ではMatcherクラスを使用している。
import java.util.regex.Matcher; import java.util.regex.Pattern;
String regexp = "ab.*ef"; String test = "abcdef"; Pattern pt = Pattern.compile(regexp); Matcher m = pt.matcher(test); System.out.println(m.matches());
「.
」はどんな文字でもマッチするのだが、デフォルトでは改行コードにはマッチしない。
改行コードにもマッチさせたい場合は、Pattern#compile()の第2引数にDOTALLフラグを指定する。
Matcher m; m = Pattern.compile("ab.*ef").matcher("abc\ndef"); System.out.println(m.matches()); →false m = Pattern.compile("ab.*ef", Pattern.DOTALL).matcher("abc\ndef"); System.out.println(m.matches()); →true
matches()は文字列全体がパターンにマッチしているかどうか判断するものだが、find()では、文字列の一部がパターンにマッチしているかどうかを判定する。
(いわばStringのcontains()相当)
//「123」が含まれているかどうか
Matcher m = Pattern.compile("123").matcher("abc123def123ghi");
System.out.println(m.find());
→true
find()によって一致する文字列が見つかった場合、Matcherはその位置を保持している。[2009-02-20]
find()によって複数の文字列がマッチする場合もあるが、find()を複数回呼ぶことでマッチした位置を全て取得することが出来る。
Matcher m = Pattern.compile("123").matcher("abc123def123ghi");
while (m.find()) {
// 見つけた文字列と、その位置
System.out.println(m.group() + ": " + m.start() + "/" + m.end());
//JDK1.5
// MatchResult mr = m.toMatchResult();
// System.out.printf("%s: %d/%d\n", mr.group(), mr.start(), mr.end());
}
↓
123: 3/6
123: 9/12
※find()を呼ぶ度にMatcherインスタンス内のstart/endは変わっていくが、MatchResult(JDK1.5以降)は独立したインスタンスとなっているので変化しない。
(ただしMatchResultはstart/end以外のデータもコピーして保持するので、start/endしか使わないならちょっとコストが高いかも)
find()で見つけた位置の文字列はgroup()で取得することが出来る。[2014-09-28]
Matcher m = Pattern.compile("[0-9]+").matcher("abc123def456ghi"); while (m.find()) { String s = m.group(); System.out.println(s); }
また、パターンを丸括弧で囲むことにより、その部分を取得することが出来る。
Matcher m = Pattern.compile("abc([0-9]+)def([0-9]+)(.*)").matcher("abc123def456ghi"); if (m.matches()) { System.out.println(m.group(0)); //→abc123def456ghi System.out.println(m.groupCount()); //→3 for (int i = 1; i <= m.groupCount(); i++) { String s = m.group(i); System.out.println(s); } }
↓実行結果
abc123def456ghi 3 123 456 ghi
group(0)はgroup()と同等で、マッチしたパターン全体を返す。
なので、丸括弧で囲んだ部分を表す添字は1から始まる。
find()の場合も丸括弧で囲んでおくと、その部分を取得することが出来る。
String test = "abc123def456ghi"; Pattern pattern = Pattern.compile("([a-z]+)([0-9]*)"); Matcher m = pattern.matcher(test); while (m.find()) { System.out.println("group=" + m.group()); System.out.println("count=" + m.groupCount()); for (int i = 1; i <= m.groupCount(); i++) { System.out.println("[" + m.group(i) + "]"); } }
↓実行結果
group=abc123 count=2 [abc] [123] group=def456 count=2 [def] [456] group=ghi count=2 [ghi] []
なお、「*
」でパターンを指定している箇所が0文字でマッチした場合は、空文字列が返ってくる。[2015-03-22]
JDK1.7で、groupメソッドに名前を指定できるようになった。[2015-03-22]
正規表現内に名前を指定しておくことで、その名前を使って値を取得できる。
(start/endメソッドで名前が指定できるのは、JDK1.8から。[2015-06-14])
名前は、取得したい値の丸括弧内の先頭に「?<名前>
」という形で指定する。
(名前に使えるのは英数字のみ(記号は使えない)[2019-12-08])
String test = "abc123def456ghi"; Pattern pattern = Pattern.compile("(?<alphabet>[a-z]+)(?<number>[0-9]*)"); Matcher matcher = pattern.matcher(test); while (matcher.find()) { System.out.printf("[%s][%s]%n", matcher.group("alphabet"), matcher.group("number")); }
↓実行結果
[abc][123] [def][456] [ghi][]
find()でマッチした文字列の位置をstart(), end()で取得することが出来る。[2015-06-14]
String test = "abc123def456ghi"; Matcher m = Pattern.compile("[0-9]+").matcher(test); while (m.find()) { // 見つかった文字列 String s = m.group(); // 見つかった位置 String t = test.substring(m.start(), m.end()); System.out.println(s.equals(t)); }
パターンを丸括弧で囲むことにより、その部分を取得することが出来る。
String test = "abc123def456ghi"; Pattern pattern = Pattern.compile("([a-z]+)([0-9]*)"); Matcher m = pattern.matcher(test); while (m.find()) { System.out.println("group=" + m.group()); System.out.println("count=" + m.groupCount()); for (int i = 1; i <= m.groupCount(); i++) { System.out.println("[" + m.group(i) + "] " + m.start(i) + "/" + m.end(i)); } }
↓実行結果
group=abc123 count=2 [abc] 0/3 [123] 3/6 group=def456 count=2 [def] 6/9 [456] 9/12 group=ghi count=2 [ghi] 12/15 [] 15/15
JDK1.8で、start/endメソッドに名前を指定できるようになった。
(groupメソッドでは、JDK1.7で名前を指定できるようになっていた)
String test = "abc123def456ghi"; Pattern pattern = Pattern.compile("(?<alphabet>[a-z]+)(?<number>[0-9]*)"); Matcher matcher = pattern.matcher(test); while (matcher.find()) { System.out.printf("%s: %d/%d%n", matcher.group("alphabet"), matcher.start("alphabet"), matcher.end("alphabet")); System.out.printf("%s: %d/%d%n", matcher.group("number"), matcher.start("number"), matcher.end("number")); }
↓実行結果
abc: 0/3 123: 3/6 def: 6/9 456: 9/12 ghi: 12/15 : 15/15
find()でマッチする文字列を別の文字列に変換することも出来る。
replaceFirst()ではマッチした最初の1文字列、replaceAll()はマッチする全ての文字列を置換する。
//「123」を「ZZZ」に置換する Matcher m = Pattern.compile("123").matcher("abc123def123ghi"); System.out.println(m.replaceFirst("ZZZ")); System.out.println(m.replaceAll("ZZZ"));
↓実行結果
abcZZZdef123ghi abcZZZdefZZZghi
文字列を指定された文字で分割して配列を作るString#split()も、指定するのは正規表現。マッチした文字で区切られる。
内部ではPattern#split()が使われている。
//カンマで分割する String[] ss = "abc,def,ghi".split(","); System.out.println(Arrays.toString(ss));
//セミコロンで分割する String[] ss = "abc;def;ghi".split(";"); System.out.println(Arrays.toString(ss));
//カンマ又はセミコロンで分割する String[] ss = "abc;def,ghi".split("[,;]"); System.out.println(Arrays.toString(ss));
※[ ]でくくると、その中のどれか一文字にマッチする。
//ピリオドで分割する String[] ss = "abc.def.ghi".split("\\."); System.out.println(Arrays.toString(ss));
※ピリオド「.
」やアスタリスク「*」は正規表現では特別な意味を持つ為、カンマやセミコロンの様にそのままでは使えない。「\」でエスケープする必要がある。
→JDK1.5以降ではPattern.quote()を使う方が便利。[2014-04-15]
内容 | コード例 | 結果例 | 説明 |
---|---|---|---|
パスの途中の日付を取得する。 [2015-08-28] |
static String getDate(String s) { |
D:/tmp/data/hoge/20150828/zzz/ |
文字列内の数値8桁部分を抽出する。 「 \\d 」は数字、「\\d{8} 」は数字が8個。マッチした場合、丸括弧部分の値がgroupメソッドで取得できる。 丸カッコ内に名前を付けているので、名前で取得できる。 |
IPアドレスを抽出する。 [2015-08-28] |
static String getIpAddress(String s)
{ |
192.168.0.1 |
文字列内にあるIPアドレス部分を抽出する。 「 \\d 」は数字、「\\d+ 」は数字が1個以上。「 \\. 」はピリオド。マッチした場合、丸括弧部分の値がgroupメソッドで取得できる。 「 \\d+ 」でマッチしているから必ず数字なので、parseIntでエラーになる事は無い。(IPアドレスの数字の範囲を正規表現で表現するのは困難なので、正規表現の外で判定している) |
192.168.0.256 |
|||
備考欄に192.168.0.1とか入れないで欲しい |
|||
複数の空白を1つに変換する。 [2015-08-28] |
static String compactBlank(String s) { |
abc \t\r\ndef |
文字列内の複数の空白(改行も含む)を1つの空白に置換する。 「 \\s 」は空白文字、いわば「[ \f\t\r\n] 」のようなもの。「 \\s+ 」は空白文字が1個以上。 |