2019/02/06
sponsored link
もくじ
正規表現とは
正規表現とは何か?
分かりやすいように例を出して説明します。
文字列が等しいかどうかを調べる
まず正規表現の説明の前に、二つの文字列(Stringオブジェクト)が等しいかどうかを調べたい場合どうするか?おさらいしておきます。
その際、使われるのがequalsメソッドです。
public boolean equals(Object anObject)
anObject - この文字列と比較したいオブジェクト(文字列)
【戻り値】
引数に渡したオブジェクトがこの文字列に等しいStringを表す場合はtrue、それ以外の場合はfalse
具体的には以下のように使います。
String a = "abc";
if(a.equals("abc")){
System.out.print("この文字列は「abc」です");
}else{
System.out.print("この文字列は「abc」ではありません");
}
出力結果→この文字列は「abc」です
String a = "abcde";
if(a.equals("abc")){
System.out.print("この文字列は「abc」です");
}else{
System.out.print("この文字列は「abc」ではありません");
}
出力結果→この文字列は「abc」ではありません
ご覧のように、equalsメソッドで調べられるのは、文字列が完全に一致しているかどうかだけでです。
しかし、完全一致ではなくもうちょっと柔軟にチェックしたい場合もあると思います。そういう場合に使えるのが正規表現です。
パターンの一致を調べる
例えば、ある文字列の中に「Java」という単語が含まれるかどうかを調べたい場合。
equalsメソッドではなく、matchesメソッドと正規表現を使って文字列の部分一致を調べることができます。matchesメソッドもStringクラスで定義されているインスタンスメソッドです。
public boolean matches(String regex)
regex - この文字列との一致を判定する正規表現
【戻り値】
引数に渡した正規表現とこの文字列が一致する場合はtrue、それ以外の場合はfalse
具体的には以下のように使います。
String a = "僕はJavaが大好きだ";
if(a.matches(".*Java.*")){
System.out.print("この文字列には「Java」が含まれています");
}else{
System.out.print("この文字列には「Java」が含まれていません");
}
出力結果→この文字列には「Java」が含まれています
macthesメソッドの引数に渡しているのが、正規表現です。matchesメソッドはその文字列と引数に渡された正規表現が合致するかどうかを調べるメソッドです。
この例では、matchesメソッドに渡された引数は
a.matches(".*Java.*")
こうなっています。ダブルクォーテーションで
.*Java.*
という文字列を囲んでいます。
正規表現は普通の文字列と同じようにダブルクォーテーションで囲む必要があります。
「Java」の左右それぞれに「.*」という記号がついていますね。後で改めて説明しますが、「.*」という記号は、「0文字以上の何らかの文字(何でもいい)」という意味があります。
要するに『.*Java.*』が指し示すのは、『「Java」を含む文字列』という意味になります。
上の例では、matchesメソッドの引数に『「Java」を含む文字列』という意味を持つ正規表現を渡しています。その結果、その文字列(a)が「Javaを含む文字列」になっているかどうかをチェックすることが出来るわけです。
一つ注意してほしいのですが、matchesメソッドは部分一致を調べる為のメソッドではありません。matchesメソッドとequalsメソッドの違いは、引数に正規表現を受け取れるかどうかの違いだと言えます。
この例では、matchesメソッドが比べているのは、
僕はJavaが大好きだ
と
.*Java.*
が完全一致しているかどうか?です。
例えば以下では、完全一致と見なされないのでmatchesメソッドはfalseを返すことになります。
String a = "僕はJavaが大好きだ";
if(a.matches("Java.*")){
System.out.print("この文字列には「Java」が含まれています");
}else{
System.out.print("この文字列には「Java」が含まれていません");
}
出力結果→この文字列には「Java」が含まれていません
(出力結果の文言が適当ではありませんがご容赦を)
「Java.*」の場合、「Java」の前に何らかの文字(例では「僕は」)があれば不一致と判定されるからです。
このように「.」や「*」のような特殊な記号を使って、文字列のパターンを表現したものを正規表現と言うわけです。
正規表現をうまく使えば、柔軟かつ厳密に文字列のチェックを行うことができます。なんとなく正規表現の使いどころが分かりますでしょうか?
では具体的に、「.」や「*」といった記号が意味するところを一つずつ説明していきます。
正規表現の記号の意味
記号に関しては、もちろん全て半角ですので注意して下さい。
記号を一つずつ説明していきます。その記号を使った正規表現の例を出して、その下に、その正規表現が示す文字列パターンに合致するかどうかの例をいくつか書きますので、まずは記号の意味するところを理解して下さい。
何でもいい一文字を表す「.」
「.」は文字一つを意味します。どんな文字でもいいのでとにかく文字一つという意味です。
"僕は....が好きだ"
○ ”僕はJavaが好きだ”
○ ”僕はおにぎりが好きだ”
× ”僕はJavaScriptが好きだ”
"...と...."
○ ”PHPとJava”
○ ”パンダとシマウマ”
○ ”とととととととと”
× ”となりのトトロ”
直前の文字の現れ方を指定する「*」「+」「?」
直前の文字が0個以上ある「*」
「*」はその直前(左)にある文字が0個以上あることを意味します。0個以上なのでその文字が無くても合致します。
"おー*い"
○ ”おい”
○ ”おーい”
○ ”おーーーーーい”
この場合、「ー」が0個以上ということになります。
先ほどの例で出てきた「.*Java.*」は、「*」の直前の文字が「.」になっています。「.」はどんな文字でもいいので、「.*」でどんな文字がいくつあってもいいし、全く無くてもいいということになります。
直前の文字が1つ以上ある「+」
「+」はその直前にある文字が1つ以上あることを意味します。
"おー+い"
× ”おい”
○ ”おーい”
○ ”おーーーーーい”
1つ以上ないといけないので、この場合「おい」では合致しないことに注意して下さい。
直前の文字が1つだけあるか無い「?」
「?」はその直前にある文字があってもなくても合致します。ただし2個以上あれば合致しません。
"おー?い"
○ ”おい”
○ ”おーい”
× ”おーーーーーい”
いずれかの文字列を表す「|」
「|」で区切ることで、いずれかの文字列という意味になります。
"暑い|寒い"
○ ”暑い”
○ ”寒い”
× ”涼しい”
「|」の両サイドにスペース等入れないようにしましょう。スペースも文字として認識されてしまいます。
3つ以上でも大丈夫です。
"暑い|寒い|涼しい"
○ ”暑い”
○ ”寒い”
○ ”涼しい”
グループを表す( )
複数の文字をくくる
例えば、既に説明したように「+」は「直前の一文字が1回以上続く」という意味になりますが、「( )」を使って文字列をくくることで一文字だけでなく複数の文字に対して「+」を効かせることができます。
"(むかし)+、ある所に"
○ ”むかし、ある所に”
○ ”むかしむかし、ある所に”
選択肢をくくる
既に説明したように、「|」で区切ることで、いずれかの文字列という意味になりますが、この選択肢を「( )」でくくってしまうと非常に使い勝手がよくなります。
"今日は(暑い|寒い|涼しい)です"
○ ”今日は暑いです”
○ ”今日は寒いです”
○ ”今日は涼しいです”
もしこの「( )」がなければ、
"今日は暑い|寒い|涼しいです"
× ”今日は暑いです”
○ ”今日は暑い”
○ ”寒い”
○ ”涼しいです”
こんなことになってしまうので注意しましょう。
この中からいずれか一文字を表す「[ ]」
[ ]で囲うことで、その囲われた中のうちいずれか一文字という意味になります。
[ ]内は一文字ずつ認識される
区切り等は必要ありません。
"今日は[月火水]曜日だ"
○ ”今日は月曜日だ”
○ ”今日は火曜日だ”
○ ”今日は水曜日だ”
× ”今日は日曜日だ”
あくまで、[ ]の中の文字の内、どれか一文字だけです。
"好きな漢字は[焼肉定食]です"
○ 好きな漢字は肉です
○ 好きな漢字は定です
× 好きな漢字は焼肉です
× 好きな漢字は焼肉定食です
英単語で頭文字が大文字でも小文字でも良い場合などは以下のように書きます。
"[Jj]ava"
○ ”Java”
○ ”java”
× ”JAVA”
× ”Dava”
正規表現ではスペースやコンマも文字として認識されてしまいます。少し読みにくいですが、原則としてつなげて書く必要があります。もし、コンマを入れてしまうと、
"[J,j]ava"
○ ”Java”
○ ”java”
× ”JAVA”
○ ”,ava”
このようにコンマもただの文字として認識され、「J」か「,」か「j」という意味になってしまいます。
数字やアルファベットをまとめて指定する「-」
数字一文字を表す場合は、
"[0123456789]"
↑これでもいいのですが、長ったらしいので以下のように省略して書くことができます。
"[0-9]"
↑これで0から9までの中から一文字という意味になります。[ ]の中で使われる「-」は普通の文字(ハイフン)ではなく特殊な意味を持つ記号として扱われます。
このハイフンを使えば、アルファベット(小文字)のどれか一文字というのも簡単に作れます。
"abc[a-z]"
○ ”abcd”
○ ”abck”
× ”abcA”
× ”abc”
アルファベットもしくは数字を意味したければつなげて書くこともできます。
"abc[a-zA-Z0-9]"
○ ”abcd”
○ ”abck”
○ ”abcA”
× ”abc”
○ ”abc5″
ハイフンはただ単に「から」というような意味なので、何も初めから最後までじゃなくても大丈夫です。途中の数字やアルファベットでも問題なく使えます。
"彼女いない歴[5-9]年です"
○ ”彼女いない歴6年です”
○ ”彼女いない歴9年です”
× ”彼女いない歴1年です”
ただし、ハイフンが「から」という意味として認識されるのは、数字どうしでハイフンを挟むか、アルファベットどうしでハイフンを挟んだ場合のみです。
※厳密には、数字→大文字アルファベット→小文字アルファベットの順番でつなげて指定することも出来ます(順番が間違っていると例外が投げられるので注意)。
例 [0-z] = [0-9A-Za-z]
数字やアルファベットで挟まなければ、ハイフンはただの文字としてのハイフンとして認識されます。
"[01-]"
○ ”0″
○ ”1″
○ ”-“
× ”2″
[ ]の中は一文字ずつ認識されるという大前提を忘れないようにしましょう。やろうと思えば以下のような使い方も出来ます。
"好きな数字は[16-9]です"
○ ”好きな数字は1です”
○ ”好きな数字は7です”
× ”好きな数字は2です”
× ”好きな数字は5です”
「1か6~9」という意味になります。「16~9」ではないので注意が必要です。
[ ]内で否定を表す「^」
[abc]はa、b、cの内どれか一文字という意味ですが、a、b、c以外のどれか一文字という表現を作ることも出来ます。「^」を使います。
"好きな数字は[^49]です"
○ ”好きな数字は1です”
○ ”好きな数字は7です”
× ”好きな数字は4です”
× ”好きな数字は9です”
[ ]の初めに「^」を付けることで、[ ]内の文字以外という意味になります。「^」の右隣の一文字だけでなく[ ]内全体に否定の意味がかかってくるので注意して下さい。
メタ文字のエスケープ
さて、ここまでいろんな記号を使って文字列パターンを表現する方法を説明しました。ざっと並べるとこんな感じです。
.
*
+
?
|
(
)
[
]
正規表現内でこれらの文字を使えば、特別な意味を持つようになっているわけなんですが、これらの文字を普通の文字として正規表現内で使いたい場合もあると思います。
例えば、「OK?」という文字と合致しているか調べたい場合。
"OK?"
× ”OK?”
○ ”OK”
○ ”O”
「?」は直前の一文字が一つだけあるor無いという意味にとられるので、この場合、「K」が一つだけあるor無いという意味を示す正規表現になることになります。
正規表現において意味を持つ記号を、普通の文字として使いたい場合は、¥を使ってエスケープする必要があります。
¥?
ただし、¥一つだとエスケープ記号と認識されるので、この場合、
¥¥?
と二つ続けて書く必要があります。
先ほどの例をエスケープを使って正しく書き直すと、こうなります。↓
"OK¥¥?"
○ ”OK?”
× ”OK”
× ”O”
上記で羅列した正規表現の中で特別な意味を持つ記号を普通の文字として使いたい場合は、このようにバックスラッシュ(円記号)を使ってエスケープする必要があります。
正規表現のすべて
残念ながら、これが正規表現の全てではありません。とりあえずは正規表現とはどんなものなのか?は掴んでもらえたと思います。いろいろ自分で書いてみて、記号の意味するところを直感的に分かるようになれば便利に使いこなせると思います。
正規表現についての詳細はこちらをご覧になってください。
今だけ→転職できなければ全額返金の「エンジニア転職保証コース」
絶対エンジニアになる!→テックエキスパート
フリーランスエンジニアの収入例を見てみる→レバテックフリーランス
コメント
「パターンの一致を調べる」のmatchesメソッドの定義を紹介している箇所がequalsのままになってますよ.
by ebitiri 2016/10/18 13:02
>ebitiriさん
ご指摘ありがとうございます。感謝です。
by Nobuo@管理人 2016/10/18 13:08