職業訓練日記 -PHPにおける正規表現-

PHP

こんにちは!ゆきまさです!!(@yyykms

今回は職業訓練で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$メタ文字を検索対象文字列の終りにのみマッチさせる
対象文字列の末尾に改行がある場合マッチしない
※D未指定時でかつ最後の文字が改行の場合、改行の直前の文字も$メタ文字の対象になる
※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回だけマッチとなっていますが、要するに「-(ハイフン)はあってもなくてもいいけど、あった場合は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文字以上となるが、なくてもよい。

となります。

「.a」「.abc」「.ab.cd」「.ab.cd.ef」などはOKで、「a..b」「..ab」「abc.」などドットが続いたり最後になるのはNGになります。

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

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

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

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

一回以上なので「@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