フォームを扱うことが多いPHPでは、正規表現はしょっちゅう使うことになる。本当にしょっちゅうだ。
電話番号を判定したり、メールアドレスが正しいか確認したり、YouTubeのURLからapiデータを引っ張ってきたりと正規表現は様々なところで活躍する。
このページではPHPの正規表現についてまとめた。参考にしていただければと思う。
目次
PHPにおける正規表現とは?
まずは、正規表現そのものについて簡単にご紹介しよう。PHPではフォームのチェックやインポートデータの確認など、ユーザがWebから入力した何かのチェックに使うことが多い。
正規表現とは?
正規表現とは、日本語のままではイメージしづらいが、英語では「Regular Expression」という単語で表されている。もしかしたら、こちらの方がイメージしやすいかもしれない。
特別なルールで決められた特別な文字の組み合わせを使って実現させる式のことだ。
説明が難しいのでもっと容易に説明すると、、、
正規表現とは?その2
「こんな部分の文章だけ抜き出したい!」という要望に応えるものだ。
例えば、下記のような文章があったとしよう。
- リスキルテクノロジーの住所は〒160-0022 東京都新宿区新宿3-1-13 京王新宿追分ビル4階です
- 大阪府庁の住所は〒540-8570 大阪市中央区大手前2丁目 大阪府庁本館1階です
この中で、郵便番号だけを抜き出して、別のファイルに書き込みたいとする。人間だったらすぐに判定がつくはずだ。
「郵便番号って〒があって、その後に3つの数字があって、次に-が書いてあって、その後に4つの数字がある部分だよね」
これをプログラムが判断できるようにしたのが正規表現だ。下記のような用途、機能を実現させるために利用できる。
- 特別な文字や記号で校正された検索式
- 対応するツール、プログラム言語共通の検索式
- 文字列に対して検索、置換処理が可能
さっそく正規表現を行う上で定番の知識についてみていこう。
正規表現の基本的なしくみ
文字列に対する正規表現パターンマッチは、下記のように動作する。
- ステップ1. 文字列の先頭から調べる
- ステップ2. 次にその位置でマッチしなかった場合は、次の文字に移動して再度調べる
- ステップ3. 途中でマッチした場合は、その位置でマッチング作業を終える
- ステップ4. マッチするものがなければ、文字列を最後まで調べて終わる
左から始まりマッチした時点で終了する、というのが一般的だ。下記の例では、 catのcaにマッチしたところで正規表現は終了となり、後ろのCaliforiaにはマッチしない。
正規表現 : ca
文字列 : My cat’s name is Califoria
後ほど見ていくが、マッチしているかをtrue or falseで返したり、マッチした場所を数字で返す関数が用意されている
正規表現の例
細かいところに入っていく前に実際にプログラムを動かしてみるとわかりやすいだろう。
1 2 3 4 |
<?php echo preg_match("/c[aeiou]t/", "I love my cat"); echo preg_match("/c[aeiou]t/", "I love my dog"); ?> |
preg_matchは第一引数の正規表現に第二引数のテキストが一致していたら、trueを返してくれる関数だ。
/c[aeiou]t/
が正規表現に当たる。これは、「1文字目がcで、2文字目がa,e,i,o,uのどれかで、3文字目がt」の文章があるかどうか?という正規表現だ。「cat」はまさしくそれにあたるが、"I love my dog"にはそれに当たる文字が無い。なので結果は、
1 2 |
1 0 |
となる。
基本的な正規表現
よく使われる正規表現の一覧をご紹介していこう。後ほど特に使うものは例も踏まえてご紹介する。
^ |
行頭にマッチする |
例: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回以上繰り返す |
例:a、ab、abb、abbbなどはマッチする。 |
+ |
直前の表現を1回以上繰り返す |
例: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..などの数字によるグループへのアクセスできる。 |
|
[あ-ん] |
ひらがなの1文字にマッチする。 |
例:1、"あいう".matches("[あ-ん]*")。trueを返す。 |
文字クラス[]
基本的な使い方
[]を文字クラスと呼び、囲まれた文字のどれか1文字にマッチする表現となる。たとえば[12]という正規表現の場合は、1もしくは2にマッチする。
[abcdefg]
とすると、a、b、c、d、e、f、gの何れかの文字という意味だ。
一括で範囲を表す
さらに[]の中で、-を使うと範囲をあらわせる。たとえば、[1-5]とした場合は、1,2,3,4,5にマッチする。
[0-9] 0123456789のいずれか1文字にマッチ
[a-zA-Z0-9] 01〜9ab〜zAB〜Zのいずれか1文字にマッチ
否定
また文字クラスの中で ^ を使うと否定になる。
[^0-9] 数字以外の1文字にマッチする。
文字列クラスでマッチするのは、あくまで1文字だ。
量指定子
量指定子と呼ばれるものが、4つある。これはその前の要素を何個繰り返すか?という指定だ。
それぞれ見ていこう。
?
「?」は直前の要素が0個か1個の場合にマッチする。
https?
直前のsに対してマッチするので、httpかhttpsがマッチ対象となる
*
アスタリスク「*」は直前の要素が0個以上繰り返し存在する場合にマッチする。
go*d
oに対してマッチしようとするので、 god や gd や good がマッチする。
+
「+」は直前の要素が1個以上繰り返し存在する場合にマッチする。
o+
oという文字が1個以上連続する場合なので god や goodやgoooodにマッチする。
{n} {n,} {m,n}
直前の要素の繰り返しの数を指定する。
a{5}
aが5個繰り返されるので、 aaaaaにマッチする。
a{1,3}
a や aa や aaaにマッチする。
a{1,}
aが1以上繰り返されるものにマッチする。
選択
() には3つの役割がある。
- 他の正規表現の一部として利用する
- 正規表現の有効範囲を指定する機能
他の正規表現の一部として利用する場合
(red|white|yellow)手袋
これはred手袋、white手袋、yellow手袋のいずれかにマッチする。「|」は複数の選択肢のいずれかにマッチさせる場合に利用する。
ここにおける()は、手袋という他の正規表現との一部として機能している。
正規表現の有効範囲を指定するケース
php(man)?
これはphpかphpmanのいずれかにヒットする。
manを一括りにした正規表現としてあらわしており、これをグループ化と呼ぶ。
位置に対するマッチ
位置を指定する正規表現もいくつかある。
^
「^」は文字列の行頭にマッチする。ポイントとしては、マッチするのは、先頭の文字ではなく、行頭だということだ。
^cat
category や cat 1 にはマッチするが、tomcatにはマッチしない。
$
「$」は文字列の末尾にマッチする。
¥
「¥b」は単語境界にマッチする。
blue¥b
bluejeansにはマッチせず、blueにマッチする。
正規表現は検索対象の文字列の隣接する2文字をみて、単語を構成する文字とそうでない文字が並んでいる場合にはそれを単語の境界とみなす。
そうでない文字とは、空白文字やタブなどの文字のことだ。
上記が、アプリケーション言語に関係なく、正規表現を利用するために、知っておきたい基礎項目だ。
PHPがサポートしている正規表現
では、PHPから正規表現を扱うに必要な知識をまとめていく。
PCREとPOSIX
PHPには大きく2種類の正規表現エンジンが用意されている。1つはPerl互換のPCRE、もう1つはPOSIX拡張だ。
PCRE正規表現を扱う関数はpregで始まり、POSIX拡張正規表現を扱う関数はeregで始まる。ereg系の関数はバイナリセーフではないため、セキュリティの観点からもオススメできず、PHP5.3以降では原則非推奨となっている。
ereg系は、バイナリセーフでないこと、preg系よりも処理効率が悪いなどの点もあり、一般的にPHPで正規表現を扱う際はPCRE系を利用することとなるだろう。
文字コードの問題
実は、preg系で処理できるのはUTF-8だけだ。
今やPHPアプリケーションのほとんどはUTF-8の文字コードで記載されているが、それでもSJISやEUC-JPなど古いアプリケーションにはそういった文字コードで動いているものもある。それらのマルチバイト文字列を処理したい場合はどうすればよいだろうか?
そんな時のために、PHPにはもう1つ正規表現エンジンがある。
それが、mb_ereg系と呼ばれるものだ。文字コードがUTF-8以外のマルチバイト文字列に対して正規表現を行いたい場合は、こちらを利用しよう。
mb_ereg系はereg系とは異なり、バイナリセーフなため、またPHP5からは内部の正規表現検索エンジンがマルチバイトに強い「鬼車」に変わった為、安心して利用できる。
PCRE正規表現構文について
PCRE正規表現を扱うための構文を紹介しよう。
デリミタ
記述したパターンはデリミタで囲む必要がある。一般的には / (スラッシュ)を利用することになる。
メタ文字
正規表現でパターンを表す為の特殊な文字をメタ文字と呼ぶ。
主に下記の通り。
メタ文字 |
説明 |
\ |
多目的に使う一般的なエスケープ文字 |
^ |
検索対象(複数行モードでは行)の始まりを言明 |
$ |
検索対象の終わりあるいは終端の改行文字の前(複数行モードでは行の終わり)を言明 |
. |
改行を除くすべての文字にマッチ(デフォルト時) |
[ |
文字クラス定義の開始 |
] |
文字クラス定義の終了 |
| |
選択肢の開始 |
( |
サブパターンの開始 |
) |
サブパターンの終了 |
? |
( の意味を拡張/0 または 1 回マッチ/なるべく少ない回数だけマッチ |
* |
0 回以上の繰り返し |
+ |
1 回以上の繰り返し |
{ |
最小/最大を指定する量指定子の開始 |
} |
最小/最大を指定する量指定子の終了 |
PHPでの正規表現の使い方
PHPで利用される正規表現の関数について見ていこう。
preg_match
preg_matchの使い方の定義は下記のようになる。
第一引数 : 正規表現を記述する。
第二引数 : パターンマッチの対象となる文字列を指定する。
第三引数 : 配列変数を渡す。パターンマッチに成功した場合はその配列変数に値が格納される。
戻り値 : マッチに成功した場合は1 、失敗は 0が返る。
1 2 3 4 5 |
<?php $text = 'My name is Tom'; $reg = preg_match('/\btom\b/i',$text,$matches); var_dump($matches); ?> |
ここでは、tom という文字列を大文字小文字関係なくマッチしたものを取得する正規表現だ。結果は下記のようになる。
1 |
array(1) { [0]=> string(3) "Tom" } |
preg_match_all
preg_matchは一致するものがあった時点で検索をストップする。だが、マッチするものを全て取得したい場合もあるだろう。そんな時は、preg_match_allを使おう。
1 2 3 4 5 |
<?php $text = 'My name is Tom. I heard You are also Tom, right? No, I am Tommy.'; $reg = preg_match_all('/\btom\b/i',$text,$matches); var_dump($matches); ?> |
文中には、 Tom と tom の2つがありそれら全てがマッチしてほしい。ちなみに、Tommyはマッチしないので注意だ。(¥bなので)
結果は下記の通り、Tomとtomがマッチした。
1 |
array(1) { [0]=> array(2) { [0]=> string(3) "Tom" [1]=> string(3) "Tom" } }<br> |
ほぼpreg_matchと同じような利用方法だが、matchesに返却されるのが2次元配列という点が異なる。
preg_replace
検索が出来れば、置換も出来る。それがpreg_replaceだ。
1 2 3 4 5 |
<?php $text = 'I like a dog'; $reg = preg_replace('/a (.*)/i', 'a $1 race', $text); echo $reg."\n"; ?> |
like a とそれに続く文字列を書き換えるケースだ。結果は下記のようになる。
1 |
I like a dog race |
preg_replaceも使い方はpreg_matchとほとんど同じだ。
第一引数 : 正規表現を記載する。
第二引数 : 置換後の文字列を指定する。区切り文字の / などは不要だ。
第三引数 : パターンマッチの対象となる文字列を指定する。
第四引数 : (省略可能)1つだけ置換したい場合は1を指定。
まとめ
このページではPHPの正規表現についてまとめてきた。
はじめは何を言っているのかわからないかもしれないが、必要な状況に会えば必ずピンとくるはずだ。そのときには、再度見直してみてほしい。
コメント