🪬

PHPの正規表現

2019/04/13に公開

今回は PHP を学習する中で、正規表現について学んだのでまとめがてら紹介していきます。

正規表現とは

正規表現とは様々な文字列のパターンを一つの形式で表現する方法の一つです。

このパターンを決めることによって、パターンにマッチする複数の文字列の集合を、シンプルに表現できます。

例えば、郵便局は 3 桁と 4 桁の数字(123-4567)で構成されています。

郵便番号をユーザが正しく入力しているか確認するには 3 桁と 4 桁の数字という構成(パターン)で入力されているかチェックします。

正規表現は指定したパターンとマッチしているかを判定するのです。

正規表現によるマッチング

では指定したパターンとマッチングしたり、しなかったりしたらどうなるのかというと、PHP で用意されている関数を使えば色々と活用できます。

  • preg_match():正規表現とマッチしたら 1 を返し、しなかったら 0 を返す。エラーの場合は false を返す
  • preg_replace()
    正規表現とマッチしたら文字を置き換える
  • preg_split()
    正規表現とマッチしたら文字列を分割する

基本的にはpreg_match()を使用すると思います。

例えば先ほど例に挙げた郵便番号ですが、3 桁と 4 桁の数字のパターンとマッチしなかったら「正しく入力してください」というような使い方になるかと思います。

上記で挙げた 3 つ以外にもいくつか用意されているので気になる人は PHPの公式マニュアル で確認してみましょう。

基本的な正規表現

正規表現の基本構文

構文 説明
. (ドット) 任意の1文字(改行文字を除く)
? 前の文字が0回か1回だけマッチ
* 前の文字を0回以上繰り返す
+ 前の文字を1回以上繰り返す
{n} 前の文字をn回繰り返す
{n, m} 前の文字をn回以上、m 回以下の繰り返し
[ ] 文字クラス[a-z]の場合は「a~z」のいずれか1文字
\d 数字のいずれか1文字 [0-9]と同じ
\w アルファベット、数字、アンダースコアのいずれか1文字 [a-zA-Z0-9]と同じ
\s 空白文字(スペース、タブ、改行など)のいずれか1文字
\S 空白以外のすべての文字のいずれか1文字
( ) パターンのグループ化
^ 文字列の始まり
$ 文字列の終わり
| いずれかの文字列(orと同じ)

文字クラスの代表的なパターン

文字クラス 説明
[0-9] 半角数字のいずれか1文字
[a-z] 半角英小文字のいずれか1文字
[A-Z] 半角英大文字のいずれか1文字
[a-zA-Z] 半角英字のいずれか1文字
[a-zA-Z0-9] 半角英数字のいずれか1文字
[abc] a,b,cのいずれか1文字
[^abc] a,b,c以外のいずれか1文字

パターン修飾子(末尾デミリタの後に記述)

パターン修飾子 説明
i アルファベットの大文字と小文字の違いを無視する
s シングルラインモード「. (ドット)」を改行文字にもマッチさせる
m マルチラインモード 行単位でマッチングを行う
^と$を行ごとにマッチさせる
u パターン文字列をUTF-8エンコードで扱う(全角文字を対応させる)
D $メタ文字を検索対象文字列の終りにのみマッチさせる</br>対象文字列の末尾に改行がある場合マッチしない<\br>※D未指定時でかつ最後の文字が改行の場合、改行の直前の文字も$メタ文字の対象になる<\br>※mをしている場合、この修飾子は無視される

正規表現の具体的な使い方

郵便番号のパターンチェック例

<?php
// チェックする郵便番号
 $str = '555-9999';
 if (preg_match('/^[0-9]{3}-?[0-9]{4}$/', $str)) {
   echo '郵便番号です';
 } else {
   echo '郵便番号ではありません';
 }

郵便番号で例を挙げると、if 文の条件式の中で正規表現が使われています。

(preg_match('/^[0-9]{3}-?[0-9]{4}$/', $str))

preg_match の第 1 引数に、パターン文字列が正規表現のルールで記述されています。

第 2 引数はチェックする文字列を記述します。郵便番号を変数$str に代入してるので、第 2 引数には$str が入ります。

パターンは文字列として記述するために「 '(シングルクォート)」で囲みます。

"(ダブルクォート)」で囲んでしまうと、変数展開されて特定条件下で動かなくなってしまいます。

なのであまり深く考えず「'(シングルクォート)」で囲むんだと覚えておきましょう。

続いて、「 '(シングルクォート)」で囲ったらパターンの最初と最後をデミリタと呼ばれる「/(スラッシュ)」で区切ります。

そしてその中に正規表現を記述していきます。分解して解説していきますね。

1. ^[0-9]{3}

^[0-9]{3}

「^」は文字列の始まりで、「[0~9]」は半角数字のいずれか 1 文字で、「{3}」が前の文字を 3 回繰り返すという意味になります。

以上をふまえると、この部分は「0~9の半角数字3文字で始まる」という意味になります。

2. -?

-?

続いてこの部分は郵便番号をつなぐ「-(ハイフン)」に「?」がくっついていますね。

「?」は前の文字が0回か1回だけマッチとなっていますが、「-(ハイフン)」は0回か1回だけ(なくてもよくて、あった場合は1回だけ)という意味になります。

この「?」は実際のフォームの仕様によって変わると思いますが、今回はユーザが数字だけ入力した場合と、「-(ハイフン)」も入力した場合と両方許可する想定です。

3. [0-9]{4}$

[0-9]{4}$

この部分は最初の箇所とほぼ同じですね。ただ今回は「$」があるので文字列の終わりという意味を持ちます。

なので、「0~9の半角数字4文字で終わる」という意味になります。

4. まとめ

以上をふまえてこれまでの説明をまとめると。

  1. 0~9の半角数字3文字で始まり
  2. -(ハイフン)はなくてもよくて、あった場合は1回だけ
  3. 0~9の半角数字4文字で終わる

となるわけです。

<?php
 // チェックする郵便番号
 $str = '555-9999';
 if (preg_match('/^[0-9]{3}-?[0-9]{4}$/', $str)) {
   echo '郵便番号です';
 } else {
   echo '郵便番号ではありません';
 }

そして、以上のチェックがすべて正しければ true となり、'郵便番号です'と出力するということになります。

メールアドレスのパターンチェック例

<?php
 // チェックするメールアドレス
 $str = 'abc.def.ghi@example.aaa.com';
// 正規表現
 $pattern = '/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/iD';
 if (preg_match($pattern, $str)) {
   echo 'メールアドレスです';
 } else {
   echo 'メールアドレスではありません';
 }

今回は preg_match()の第 1 引数は正規表現を代入した変数$pattern となっています。

'/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/iD'

郵便番号の時と違って「()」によってグループ化されていますが、同様に分解して解説していきます。

1. ^([a-z0-9\+_\-]+)

'/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/iD'

「a-z0-9」は a~z と 0~9 の半角英数字で「\+_\-」の部分は「+(プラス)」「_(アンダースコア)」「-(ハイフン)」の記号です。

「+(プラス)」と「-(ハイフン)」はパターン内で特殊な意味を持つメタ文字なのでエスケープ処理をしています。

この「[]」内は、 a~z と 0~9 の半角英数字と記号の「+」「_」「-」のいずれか1文字ということになります。 そして、「[]」に「+(プラス)」がついています。

これはエスケープされていないので、記号としての意味ではなく前の文字を 1 回以上繰り返すという構文になるので 「[]」内を1回以上繰り返すという意味です。

「^」があることもふまえて、このブロックをまとめると。

a~z と 0~9 の半角英数字と記号の「+」「_」「-」のいずれか1文字以上で始まる。

という意味になります。

2. (\.[a-z0-9\+_\-]+)*

(\.[a-z0-9\+_\-]+)*

次のグループは最初に「.(ドット)」から始まっています。これも先ほどと同様エスケープされているので、記号としての「.(ドット)」になります。

その次に続いている「[a-z0-9\+_\-]+」の部分は先ほどと同様で a~z と 0~9 の半角英数字と記号の「+」「_」「-」のいずれか1文字以上 という意味です。

なのでこのグループは 「.(ドット)」があった場合にそのあと続くのが、 a~z と 0~9 の半角英数字と記号の「+」「_」「-」のいずれか1文字以上

ということになり、グループ自体に 「*(アスタリスク)」がついているので、0回以上の繰り返しという意味になります。

まとめると。

「.(ドット)」があった場合にそのあと続くのが a~z と 0~9 の半角英数字と記号の「+」「_」「-」のいずれか1文字以上となるが、なくてもよい。

となります。

3. @([a-z0-9\-]+\.)+

@([a-z0-9\-]+\.)+

そして「@」に続くのは、a~z と 0~9 の半角英数字と記号の「+」「_」「-」のいずれか1文字以上の後に「.(ドット)」がある。そしてこのパターンが一回以上。

という意味です。「sample@gmail.com」の「@gmail.」の部分ですね。

1 回以上なので「@yahoo.co.」でもいいということです。

4. [a-z]{2,6}$

[a-z]{2,6}$

ここはもうわかると思いますが、 a~z の半角英字のいずれかが、2文字以上6文字以下という意味になります。

「sample@gmail.com」の「com」の部分です。

5. iD

少し長くなりましたが、以上でメールアドレスの正規表現ができました。

しかし最後のデミリタのあとに「iD」がくっついていますね。

これはアクセス修飾子といって、「'(シングルクォート)」で囲ったすべてに意味を持たせます。

「i」は「アルファベットの大文字と小文字の違いを無視する」となっていますが、ユーザーがアドレスを記述する際に、小文字と大文字を間違えてもエラーとならないようにしているのです。

「D」は「$」の後に改行があった場合はNGとしています。

6. まとめ

これですべてのチェックが終わったので、簡単にまとめると。

  1. a~z と 0~9 の半角英数字と記号の「+」「_」「-」のいずれか1文字以で始まり
  2. 無くてもいいが、「.」があった場合はそのあとに続くのがa~zと0~9の半角数字と記号の「+」「_」「-」のいずれか1文字以上となる。
  3. @のあとはa~zと0~9の半角英数字と記号の「+」「_」「-」のいずれかが1文字以上で、その後に「.(ドット)」があるというパターンが1回以上で、
  4. 最後はa~zの半角英字のいずれかが、2文字以上6文字以下という意味。
  5. そしてアドレスの大文字と小文字の間違えは無視し、最後の改行は拒否する。

ということです。

文字だけで表すと長いし少しややこしくなってしまいますが、順番に考えていくと案外簡単です。

<?php
 // チェックするメールアドレス
 $str = 'abc.def.ghi@example.aaa.com';
// 正規表現
 $pattern = '/^([a-z0-9\+_\-]+)(\.[a-z0-9\+_\-]+)*@([a-z0-9\-]+\.)+[a-z]{2,6}$/iD';
 if (preg_match($pattern, $str)) {
   echo 'メールアドレスです';
 } else {
   echo 'メールアドレスではありません';
 }

アドレスを変えてみたり、正規表現を変えてみたりして、自分で試してみてください。

まとめ

記号が多くそれぞれに意味を持つので覚えるのが大変ですが、いろんな場面で出てきますので頑張って使いこなせるようになってください。

ここで紹介した記号以外にもいくつかあるので気になる人は調べてみてください。

また同時に関数の使い方も一緒に覚えておきましょう。

参考になった記事も紹介しておきます。

https://qiita.com/tsuuuuu_san/items/b88b0662426f2b956c77
https://wepicks.net/phpref-regular_expression/

Discussion