正規表現はどうしても単発で使うことが多く、なかなか覚えられないという人も多いのではないだろうか?
このページではJavaの正規表現について一通りまとめている。基本的な使い方や、正規表現の一覧、URL・郵便番号などのよく使う書式など、必要なものは概ね揃っているだろう。
初心者の方が一通り勉強するのに丁度いい量でまとめた。実際にJavaで正規表現を使うときにも参考にしていただければと思う。
目次
Java正規表現の基本
正規表現とは
正規表現とは、文字列の規則性の表現手法のことを言う。
例えば、「qwertyuiop」のような何も特徴の無い文字列であっても、「半角英字」という規則性を持っている。
規則性にも様々なものがあり、「英数字」や「数字で始まる」など、どんなパターンでも「規則性」として考えることができる。
正規表現は、こういった規則性を表す数式のようなもので、一般的には「ある文字列が、指定の規則に則っているか?」といった判断材料として利用される。
ここでは、Javaでの正規表現の扱い方を、様々な例を用いて紹介する。
Javaでの正規表現の利用方法
Javaで正規表現を利用する、最もポピュラーなケースは、以下の様な処理だろう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import java.util.regex.Pattern; public class Main { public static void main(String[] args) { String str = "abcdefg" ; String pattern = "^ab" ; Pattern p = Pattern.compile(pattern); if(p.matcher(str).find()){ System.out.println(str + "はabで始まります"); }else{ System.out.println(str + "はabで始まりません"); } } } |
この例は、文字列strが、文字列「ab」で始まっているかどうかを判定している。
試してみるとすぐにわかるが、結果は
abcdefgはabで始まります
と表示される。
まず7行目だが
Pattern p = Pattern.compile(pattern);
これは変数patternに含まれる文字列「^ab」を正規表現として「コンパイル」している。
ここで、「コンパイル」とは、「プログラム内の式に翻訳している」と考えれば良いだろう。
正規表現の「^ab」は、「文字列の先頭がabである」という規則性を表している。
次に、8行目の
p.matcher(str).find()
だが、これは文字列strt(値は「abcdef」)を、正規表現の規則性に則っているかどうかを判定しており、規則性に則っている場合はtrue、そうでない場合はfalseが返ってくる。
つまり、「文字列の先頭がabである」とい規則に則っているかどうか?という判定だ。
結果は当然trueが返ってくるので、
abcdefgはabで始まります
と表示される。
正規表現の組み合わせ
先ほどの例で記号の「^」(ハット記号)は、文字列の先頭を意味している。こういった表現は後ほど一括でご紹介するが、まずは基本的な使い方を見ていこう。
「^a」 → aで始まる
「^ab」 → abで始まる
と解釈される。
似たような使い方として「$」というものがあり、これは文字列の末尾を意味している。
「a$」 → aで終わる
「ab$」 → abで終わる
次のコードを動かしてみよう。
1 2 3 4 |
String pattern = "b$" ; String str = "abcdefgb" ; Pattern p = Pattern.compile(pattern); System.out.println(p.matcher(str).find()) ; |
trueが出力される
この2つを組み合わせる場合、以下のように記述する
2つの条件のどちらか一方に一致する(or)事を判定する場合
aで始まるか、bで終わるかを判定したい場合は次のようになる。
"^a|b$"
「|」(パイプ記号)は、2つの条件の「どちらか」という意味だ。複数の条件を並べることも可能だ。
2つの条件の両方に一致する(and)を判定する場合
aで始まり、bで終わるかどうかを判定したい場合には次のようになる。
"^a.*b$"
と記述する。これは少し複雑だ。
「.*」は、「.」(任意の文字)が「*」(0個以上)続くという意味だ。「.*」を記載しないと、「abが始まりと終わりのもの」となり、「ab」だけがtrueとなってしまう。
このように、複数の正規表現を組み合わせる事で、複雑な判定を行うことも可能だ。
しかし、正規表現そのものに不慣れな場合には、あえて正規表現だけで判定しようとせず、プログラムの記述方法で補うほうがわかりやすいかも知れない。
andを判定する場合
1 2 3 4 5 6 7 8 |
Pattern pattern1 = Pattern.compile("^a"); Pattern pattern2 = Pattern.compile("b$"); String str = "abcdefgb" ; System.out.println( pattern1.matcher(str).find() && pattern2.matcher(str).find() ) ; |
orを判定する場合
1 2 3 4 5 6 7 8 |
Pattern pattern1 = Pattern.compile("^a"); Pattern pattern2 = Pattern.compile("b$"); String str = "abcdefgb" ; System.out.println( pattern1.matcher(str).find() || pattern2.matcher(str).find() ) ; |
正規表現は記号の組み合わせのため、不慣れな人にはどのような判定条件なのかがソースコードを読んだだけではわかりづらい。
無理に一つの正規表現で済ませようとせず、プログラムの記述で補うほうが読み手にとってわかりやすい事も多いので注意したほうが良いだろう。
正規表現を使う場合には、コメントで何を判定しているかを残しておくことも大切だ。
特殊記号を使うときの注意
正規表現は、その表現に^や$などの記号を用いているため、$記号そのものを単なる文字として使用したい場合には、記述方法を工夫しなければならない。
以下の例を試してみてほしい
1 2 3 4 5 6 7 8 |
String str = "a$bcdefg" ; String pattern = "^a$" ; Pattern p = Pattern.compile(pattern); if(p.matcher(str).find()){ System.out.println(str + "はa$で始まります"); }else{ System.out.println(str + "はa$で始まりません"); } |
「^a$」を「a$で始まる」と解釈するのであれば「a$bcdefg」は規則通りなのだが、実際にこの例を実行すると
a$bcdefgはa$で始まりません
と表示される。
これは、正規表現中の「$」が、文字としての「$」では無く、文字列の末尾を意味する記号として解釈されてしまうからだ。
この問題を回避するためには[](角括弧)を使用する。
1 2 3 4 5 6 7 8 |
String str = "a$bcdefg" ; String pattern = "^[a$]" ; Pattern p = Pattern.compile(pattern); if(p.matcher(str).find()){ System.out.println(str + "はa$で始まります"); }else{ System.out.println(str + "はa$で始まりません"); } |
[a$]ように[](角括弧) の中に記述することで、$を単なる文字として扱うことが出来る。
$の他にも、「\」「^」など、意味を持った記号があるので、記号を判定対象の文字として使用する場合には注意する必要がある。
まず知っておきたい基本的な使い方はこんなところだ。
正規表現のまとめ
ここからはさらに幅広く、正規表現に必要な知識をご紹介していく。
様々な正規表現
正規表現の代表的なものを紹介しよう。
^ |
行頭にマッチする |
例:1、^hello。行頭にhelloの文字列はこのパターンにマッチする。 |
$ |
行末にマッチする |
例:1、hello$。行末にhelloの文字列はこのパターンにマッチする。 |
. |
改行以外の任意の1文字にマッチする |
例:1、hello.world。hello worldやhello-worldなどの文字列はこのパターンにマッチする。 |
[] |
角括弧内の任意の1文字にマッチする |
例:1、[abc]。aかb、cはこのパターンにマッチする。 |
2,r[aeu]d。radやred、rudはこのパターンにマッチする。 |
||
[A-Z] |
英大文字A-Zの任意1文字にマッチする |
例:1、a[A-Z]c。aAc,aBc,…,aZcなどの文字列はこのパターンにマッチする。 |
[a-z] |
英小文字a-zの任意1文字にマッチする |
例:1、a[a-z]c。aac,abc,…,azcなどの文字列はこのパターンにマッチする。 |
[0-9] |
数字0-9の任意1文字にマッチする |
例:1、a[0-9]c。a0c,a1c,…,a9cなどの文字列はこのパターンにマッチする。 |
[^] |
角括弧内に含まれない1文字にマッチする |
例:1、[^abc]。a、b、c以外の文字はこのパターンにマッチする。 |
+ |
直前の表現を0回以上繰り返す |
例:1、ab。aやab、abb、abbbなどの文字列はこのパターンにマッチする。 |
? |
直前の表現を0回または1回繰り返す |
例:1、ab?。aとabはこのパターンにマッチする。 |
{n} |
直前の表現をn回繰り返す。nは整数。 |
例:1、ab{2}。abbはこのパターンにマッチする。 |
{n,} |
直前の表現をn回以上繰り返す。nは整数。 |
例:1、ab{2,}。abb、abbbなどはこのパターンにマッチする。 |
{n,m} |
直前の表現をn回からm回まで繰り返す。n,mは整数。 |
例:1、ab{2,5}。abb,abbb,abbbb,abbbbbはこのパターンにマッチする。 |
a|b |
aまたはbにマッチする |
例:1、hello|world。helloまたworldはこのパターンにマッチする。 |
() |
グループ化。()内のパターンは(グループ)と見なされる。このグループに一致する文字列を記憶する。1,2..などの数字によるグループへのアクセスできる。 |
- |
\w |
任意の英数字と_ 1文字にマッチする。 |
例:1、"abc23_".matches("\w*")。trueを返す。 |
\W |
英数字と 以外の任意1文字にマッチする。 |
例:1、"abc23".matches("\W*")。falseを返す。 |
\s |
任意の空白文字(スペース、タブ、改行、復帰)1文字にマッチする。 |
例:1、" \n\r".matches("\s*")。trueを返す。 |
\S |
空白文字(スペース、タブ、改行、復帰)以外の任意1文字にマッチする。 |
例:1、" \n\r".matches("\s*")。falseを返す。 |
\d |
数字の1文字にマッチする。 |
例:1、"120".matches("\d*")。trueを返す。 |
\D |
数字以外の1文字にマッチする。 |
例:1、"120".matches("\D*")。falseを返す。 |
[あ-ん] |
ひらがなの1文字にマッチする。 |
例:1、"あいう".matches("[あ-ん]*")。trueを返す。 |
メールアドレスを判定する
文字列がメールアドレスとして適しているかどうかの判定は、新規登録機能の実装などでよく使う。メールアドレスには色々とルールがあるので、判定は意外と難解だ。
以下の様な記述で、メールアドレスの判定が出来る。
1 |
"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$" |
プログラムの記述例は
1 2 3 4 5 6 7 8 9 |
String str = "javatest@example.com" ; String pattern = "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$" ; Pattern p = Pattern.compile(pattern); if(p.matcher(str).find()){ System.out.println(str + "はメールアドレスの形式です"); }else{ System.out.println(str + "はメールアドレスの形式ではありません"); } |
のようになる。
電話番号を判定する
電話番号を判定する場合は以下のように記述する。
"^[0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4}$"
プログラムの記述例は
1 2 3 4 5 6 7 8 9 |
String str = "03-1234-5678" ; String pattern = "^[0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4}$" ; Pattern p = Pattern.compile(pattern); if(p.matcher(str).find()){ System.out.println(str + "は電話番号の形式です"); }else{ System.out.println(str + "は電話番号の形式ではありません"); } |
のようになる。
メールアドレスや電話番号の判定に用いる正規表現は、ここで紹介したもの以外にも様々なものが存在する。
代表的な正規表現
最初のうちは自分で考え出すのは厳しいので、必要に応じてネットの検索を活用して見つけてくると良いだろう。
代表的なものだけ下記に載せておいた。参考にしてほしい。
^[a-z]+$ |
半角英字 (小文字のみ) |
^[A-Z]+$ |
半角英字 (大文字のみ) |
^[a-zA-Z]+$ |
半角英字 |
^[\\w]+$ |
半角英数字 |
^(https?|ftp)(:\\/\\/[-_.!~*\\'()a-zA-Z0-9;\\/?:\\@&=+\\$,%#]+)$ |
URL |
^\\d{3}\\-\\d{4}$ |
郵便番号 |
^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$ |
メールアドレス |
^[0-9]{2,4}-[0-9]{2,4}-[0-9]{3,4}$ | 電話番号 |
正規表現の活用例
これまで紹介した正規表現は、「文字列が規則通りか?」を判定する方法だが、正規表現には他にも使い道がある。
文字列の分割
一つ目は「文字列の分割」だ。
「ある文章(=文字列)の中から、特定のパターンで区切る」といった事ができる。
1 2 3 4 5 6 |
String str = "あかまきがみあおまきがみきまきがみ" ; String pattern = "がみ" ; Pattern p = Pattern.compile(pattern); for(String substr : p.split(str)){ System.out.println(substr); } |
この例では「あかまきがみあおまきがみきまきがみ」という文章(=文字列)を「がみ」というキーワードで区切って出力している。
実行結果は
1 2 3 |
あかまき あおまき きまき |
となる。(「がみ」は含まれない)
文字列の検索
「ある文字列の中から、特定のパターンの文字列を探しだす」といった事も可能だ。
1 2 3 4 5 6 7 8 |
String str = "あかまきがみあおまきがみきまきがみ" ; String pattern = "がみ" ; Pattern p = Pattern.compile(pattern); Matcher matcher = p.matcher(str) ; while(matcher.find()){ int start = matcher.start(); System.out.println(start + "番目に見つけました"); } |
この例では、元の文章(=文字列)の何番目の文字からその対象の文字が始まっているかを出力している。(最初の文字を0番目とする)
実行結果は
1 2 3 |
4番目に見つけました 10番目に見つけました 15番目に見つけました |
となる。あくまでも文字数で数えている(バイト数では無い)事に注意する。
文字列の置き換え
「ある文章(=文字列)の中の、特定のパターンの文字列を別の文字列に置き換える」という場合には、以下のようにすれば良い
1 2 3 4 5 6 |
String str = "あかまきがみあおまきがみきまきがみ" ; String pattern = "がみ" ; Pattern p = Pattern.compile(pattern); Matcher matcher = p.matcher(str) ; String newStr = matcher.replaceAll("紙"); System.out.println(newStr); |
この例では、元の文章(=文字列)の「がみ」を「紙」に置き換えている
実行結果は
1 |
あかまき紙あおまき紙きまき紙 |
となる。
文字列の置き換えはStringオブジェクトでも同じことが出来る。
1 2 3 4 |
String str = "あかまきがみあおまきがみきまきがみ" ; String pattern = "がみ" ; String newStr = str.replaceAll(pattern,"紙"); System.out.println(newStr); |
実は、StringオブジェクトのreplaceAll()メソッドは、内部で
Pattern.compile("正規表現").matcher("元の文字列").replaceAll("置き換え文字列")
という処理を行っている。
正規表現となるパターンを何度も利用しないのであれば、StringオブジェクトのreplaceAllを使うと良いだろう。
他の言語との比較
正規表現を扱う機能を持った主なプログラム言語は、PHPやPerl、Python、Ruby、JavaScriptなどがある。Webシステムの開発でよく利用されるプログラム言語は、ほぼ対応していると考えて良いだろう。また、最近の言語は
しかし記述方法に若干の違いがあるため、注意してほしい。
例えば、文字列がaで始まるかどうかの判定を、PHPで記述すると
preg_match('[^a]', 'abcd', $m);
のように[ ] (角括弧)を使用して記述する。JavaScriptでは「/」(スラッシュ)を使用する。
正規表現のサンプルはネット上で広く展開されているので、お目当ての記述方法を見つけることは難しくは無いだろう。
しかし、紹介されているプログラム言語の違いにより、読み替えが必要となるので注意してほしい。
まとめ
このページでは、Javaの正規表現に関してまとめてきた。
基本的な使い方だけでなく、正規表現の一覧やURL・電話番号の書式などもまとめているので、必要な際に見直して活用してほしい。