補足記事

正規表現って何?

文字列が同じかどうかだけでなく、「Javaを含む」「数字が入る」といったパターンで調べたい。そんなときに使うのが正規表現です。Stringのメソッドと具体例を通して、記号の意味を一つずつ見ていきましょう。

正規表現とは

正規表現とは何か? 分かりやすいように例を出して説明します。

文字列が等しいかどうかを調べる

まず正規表現の説明の前に、二つの文字列の内容が等しいかどうかを調べたい場合はどうするか、おさらいしておきます。その際に使われるのがequalsメソッドです。

public boolean equals(Object anObject)

比較するオブジェクトを引数に渡すと、同じ文字列の内容を持つStringならtrue、それ以外ならfalseを返します。Objectクラスのequalsメソッドが、Stringクラスでは文字列の内容を比較するようにオーバーライドされています。

以下のコード例は、それぞれRegexExample.javaのmainメソッド内に書く部分です。

RegexExample.java

String a = "abc";
if (a.equals("abc")) {
    System.out.print("この文字列は「abc」です");
} else {
    System.out.print("この文字列は「abc」ではありません");
}
実行結果
この文字列は「abc」です

RegexExample.java

String a = "abcde";
if (a.equals("abc")) {
    System.out.print("この文字列は「abc」です");
} else {
    System.out.print("この文字列は「abc」ではありません");
}
実行結果
この文字列は「abc」ではありません

ご覧のように、equalsメソッドで調べられるのは文字列の内容が完全に同じかどうかです。しかし、もうちょっと柔軟にチェックしたい場合もあると思います。そういう場合に使えるのが正規表現です。

パターンの一致を調べる

例えば、ある文字列の中に「Java」が含まれるかどうかを調べたい場合。Stringクラスのmatchesメソッドに、正規表現を渡して判定できます。

public boolean matches(String regex)

引数は正規表現を表す文字列です。調べる文字列全体が、そのパターンに一致するならtrue、そうでなければfalseを返します。

RegexExample.java

String a = "僕はJavaが大好きだ";
if (a.matches(".*Java.*")) {
    System.out.print("この文字列には「Java」が含まれています");
} else {
    System.out.print("この文字列には「Java」が含まれていません");
}
実行結果
この文字列には「Java」が含まれています

引数に渡している".*Java.*"が、正規表現を表すJavaの文字列リテラルです。ダブルクォーテーションはJavaで文字列を書くためのもので、正規表現そのものは.*Java.*です。

.*前に0文字以上
Javaこの文字列
.*後ろに0文字以上

「.」は通常、改行文字を除く任意の一文字、「*」は直前の要素の0回以上の繰り返しです。改行のない文字列なら、.*Java.*で「Javaを含む文字列」というパターンになります。

ここで注意してほしいのは、matches自体は部分一致を探すメソッドではないということです。文字列全体がパターンに合うかを調べるので、「Java」の前後も.*で表しています。

RegexExample.java

String a = "僕はJavaが大好きだ";
System.out.println(a.matches("Java.*"));
実行結果
false

Java.*は「Javaで始まる文字列」を表すので、「僕は」が前にあるこの例には一致しません。このように特殊な記号を使って文字列のパターンを表現したものが正規表現です。

正規表現の記号の意味

記号はすべて半角です。まずは記号を使った正規表現と、それに一致する・しない文字列の例を比べて下さい。表の正規表現には、Javaの文字列リテラル用のダブルクォーテーションは付けていません。

何でもいい一文字を表す「.」

「.」一つで、通常は改行文字を除く任意の一文字を表します。四つ並べれば四文字です。

正規表現:僕は....が好きだ
調べる文字列matchesの判定
僕はJavaが好きだ○ 一致する
僕はおにぎりが好きだ○ 一致する
僕はJavaScriptが好きだ× 一致しない
正規表現:...と....
調べる文字列matchesの判定
PHPとJava○ 一致する
パンダとシマウマ○ 一致する
とととととととと○ 一致する
となりのトトロ× 一致しない

「とととととととと」も、最初の三文字・真ん中の「と」・最後の四文字に分けられるので一致します。

直前の要素の現れ方を指定する「*」「+」「?」

直前の要素が0回以上ある「*」

「*」は、その直前の文字などが0回以上繰り返されることを意味します。0回でもいいので、その文字が無くても一致します。

正規表現:おー*い
調べる文字列matchesの判定
おい○ 一致する
おーい○ 一致する
おーーーーーい○ 一致する

この場合、繰り返すのは「ー」だけです。先ほどの.*では「.」が繰り返されるので、文字がいくつあっても、全く無くてもよいという意味になります。

直前の要素が1回以上ある「+」

「+」は1回以上の繰り返しです。「ー」が少なくとも一つ必要になります。

正規表現:おー+い
調べる文字列matchesの判定
おーい○ 一致する
おーーーーーい○ 一致する
おい× 一致しない

直前の要素が一つだけあるか無い「?」

「?」は0回か1回を表します。あってもなくてもよいですが、二つ以上は駄目です。

正規表現:おー?い
調べる文字列matchesの判定
おい○ 一致する
おーい○ 一致する
おーーーーーい× 一致しない

いずれかの文字列を表す「|」

「|」で区切ることで、どれか一つの選択肢という意味になります。

正規表現:暑い|寒い
調べる文字列matchesの判定
暑い○ 一致する
寒い○ 一致する
涼しい× 一致しない

三つ以上でも大丈夫です。通常の設定ではスペースも文字として扱われるので、見やすくするつもりで「|」の両側にスペースを入れないようにしましょう。

正規表現:暑い|寒い|涼しい
調べる文字列matchesの判定
暑い○ 一致する
寒い○ 一致する
涼しい○ 一致する

グループを表す「( )」

複数の文字をくくる

「( )」でくくれば、複数の文字を一まとまりにして繰り返すことができます。

正規表現:(むかし)+、ある所に
調べる文字列matchesの判定
むかし、ある所に○ 一致する
むかしむかし、ある所に○ 一致する
、ある所に× 一致しない

選択肢をくくる

「|」による選択肢を「( )」でくくると、文の一部分だけを選べます。

正規表現:今日は(暑い|寒い|涼しい)です
調べる文字列matchesの判定
今日は暑いです○ 一致する
今日は寒いです○ 一致する
今日は涼しいです○ 一致する

もし、この「( )」がなければ、次のように「今日は暑い」「寒い」「涼しいです」という三つの選択肢になってしまいます。

正規表現:今日は暑い|寒い|涼しいです
調べる文字列matchesの判定
今日は暑い○ 一致する
寒い○ 一致する
涼しいです○ 一致する
今日は暑いです× 一致しない

この中からいずれか一文字を表す「[ ]」

[ ]内は一文字ずつ認識される

「[ ]」で囲むと、中に書かれた文字のうち、いずれか一文字を表します。区切りは要りません。

正規表現:今日は[月火水]曜日だ
調べる文字列matchesの判定
今日は月曜日だ○ 一致する
今日は火曜日だ○ 一致する
今日は水曜日だ○ 一致する
今日は日曜日だ× 一致しない
正規表現:好きな漢字は[焼肉定食]です
調べる文字列matchesの判定
好きな漢字は肉です○ 一致する
好きな漢字は定です○ 一致する
好きな漢字は焼肉です× 一致しない
好きな漢字は焼肉定食です× 一致しない

英単語の頭文字が大文字でも小文字でもよい場合などは、こう書けます。

正規表現:[Jj]ava
調べる文字列matchesの判定
Java○ 一致する
java○ 一致する
JAVA× 一致しない
Dava× 一致しない

コンマも区切りではなく、一つの文字として扱われます。[J,j]と書くと「Jかコンマかj」になってしまいます。

正規表現:[J,j]ava
調べる文字列matchesの判定
Java○ 一致する
java○ 一致する
,ava○ 一致する
JAVA× 一致しない

数字やアルファベットをまとめて指定する「-」

数字一文字なら[0123456789]でもよいですが、[0-9]と書けば短くなります。「-」を二つの文字の間に置いて、範囲を指定しています。

正規表現:[0-9]
調べる文字列matchesの判定
0○ 一致する
5○ 一致する
9○ 一致する
10× 一致しない
5× 一致しない
正規表現:abc[a-z]
調べる文字列matchesの判定
abcd○ 一致する
abck○ 一致する
abcA× 一致しない
abc× 一致しない

大文字や数字も含めたいなら、範囲を続けて書けます。

正規表現:abc[a-zA-Z0-9]
調べる文字列matchesの判定
abcd○ 一致する
abck○ 一致する
abcA○ 一致する
abc5○ 一致する
abc× 一致しない

初めから最後まででなく、一部の範囲でも構いません。

正規表現:彼女いない歴[5-9]年です
調べる文字列matchesの判定
彼女いない歴6年です○ 一致する
彼女いない歴9年です○ 一致する
彼女いない歴1年です× 一致しない

ハイフンそのものを候補に入れたい場合は、次のように末尾に置く書き方ができます。

正規表現:[01-]
調べる文字列matchesの判定
0○ 一致する
1○ 一致する
-○ 一致する
2× 一致しない

「[ ]」は一文字を選ぶ、という大前提も忘れないようにしましょう。

正規表現:好きな数字は[16-9]です
調べる文字列matchesの判定
好きな数字は1です○ 一致する
好きな数字は7です○ 一致する
好きな数字は2です× 一致しない
好きな数字は5です× 一致しない

これは「1か6~9」です。「16~9」ではありません。

[ ]内で否定を表す「^」

「[ ]」の先頭に「^」を置くと、中に挙げた文字以外の一文字を表します。否定は、右隣の一文字だけでなく候補全体にかかります。

正規表現:好きな数字は[^49]です
調べる文字列matchesの判定
好きな数字は1です○ 一致する
好きな数字は7です○ 一致する
好きな数字は猫です○ 一致する
好きな数字は4です× 一致しない
好きな数字は9です× 一致しない

文に「数字」と書いてあっても、[^49]は数字だけに限定してくれません。「猫」なども一致します。4と9以外の半角数字に限るなら、[0-35-8]と書けます。

メタ文字のエスケープ

ここまでに出てきた「.」「*」「+」「?」「|」「( )」「[ ]」などの、特別な意味を持つ記号をメタ文字と呼びます。特別な意味になるかは使う場所にもよります。例えば「.」は「[ ]」の中では普通の点を表します。

では、「OK?」という文字列そのものと一致させたい場合はどうでしょうか?

正規表現:OK?
調べる文字列matchesの判定
OK○ 一致する
O○ 一致する
OK?× 一致しない

このままでは「Kが一つだけあるか無い」という意味になってしまいます。普通の「?」として扱うには、バックスラッシュを前に置いて、正規表現をOK\?と書きます。これをエスケープと言います。

ただし、Javaの文字列リテラルの中では、バックスラッシュ一つを表すために二つ書く必要があります。正規表現とJavaの文字列という、二段階のルールがあるわけです。

RegexExample.java

String a = "OK?";
System.out.println(a.matches("OK\\?"));
実行結果
true
正規表現:OK\?
調べる文字列matchesの判定
OK?○ 一致する
OK× 一致しない
O× 一致しない
段階書き方・内容
Javaのソースコード"OK\\?"
matchesに渡される正規表現OK\?
一致させたい文字列OK?

環境によってバックスラッシュが円記号のように表示されることもありますが、使う文字は半角のバックスラッシュ(U+005C)です。見た目の似た円記号を別の文字として入力しないようにしましょう。

正規表現のすべて

残念ながら、これが正規表現のすべてではありません。とりあえずは、正規表現とはどんなものなのかを掴んでもらえたと思います。いろいろ自分で書いてみて、記号の意味するところを直感的に分かるようになれば、便利に使いこなせると思います。

さらに詳しい構文は、Java公式ドキュメントのPatternクラスをご覧下さい。

全講座の目次へ戻る

質問・コメント

2件

この記事についての質問や、学習して気づいたことをお寄せください。

コメントを書く

コメントの受付は準備中です。入力しても送信・保存されません。

コメントと一緒に公開される名前です。
3,000文字以内。パスワードなどの個人情報は書き込まないでください。

コメントは管理者の承認後に公開されます。

この記事のコメント

旧サイトに寄せられたコメントを、投稿日時の新しい順に掲載しています。本文・コードは当時の投稿内容です。現在の記事では訂正済みの内容や、旧環境についてのやり取りも含まれます。

  1. Nobuo@管理人
    >ebitiriさん ご指摘ありがとうございます。感謝です。
  2. 「パターンの一致を調べる」のmatchesメソッドの定義を紹介している箇所がequalsのままになってますよ.