PHP実習:正規表現の構文と練習
今夜は正規表現の基本構文について。だいぶ複雑だけど、書きながら慣れていこう。
第9章-02
正規表現において文字を表す構文は以下の通り。
.(ドット)...任意の一文字 例:/b..k/→bookがマッチ
[ ]...文字クラス。[a-z]で「A~Zまで」を示す 例:/p[a-g]n/→penがマッチ
\d...数字[0-9]と同じ 例:/product\d/→product7などがマッチ
\s...タブ、スペース、改行などの空白文字 例:/R\sL/→「R L」にマッチ
\S...すべての文字(\s以外) 例:/\S/→R、=、?などにマッチ
\w...大文字小文字のアルファベット、数字、アンダーバー
\W...非単語文字(\w以外) 例:/\W/→「-」、「&」などにマッチ
この中では特に[ ]や\dを覚えておく。 [abc]とすると「abcのうちどれか1文字」、[a-g]とすれば「アルファベット順にaからgまでのどれか1文字」を表す。大文字と小文字は区別されるので、大文字を含めたい場合は「a-gA-G」と書く。「^」を使えば否定を表現することも可能。「^a-g」と書けば「aからg以外」を表す。また、「もしくは」を意味する「|」を使用することもできる。(090|080)と書けば、「090か080のどちらか」という意味になる。
繰り返しを表現する構文は以下の通り。
?...0回か1回 例:/r[a-z]?c/→「rec」「rc」などにマッチ
*...0回以上の繰り返し 例:/po*l/→「pool」「pl」などにマッチ
+...1回以上の繰り返し 例:/co+l/→「cool」などにマッチ、「cl」はダメ
{n}...n回の繰り返し 例:/w[a-z]{2}e/→「wake」「wipe」などにマッチ
{m,n}...m回以上、n回以下の繰り返し 例/str[a-z]{2,4}/→「street」「straight」
「文字を表す構文」に続けて記述することで繰り返しを表現する。{}で回数を指定することも可能。{m,n}について、{m,}と書いた場合は単純にm回以上となり、上限がなくなる。
パターン修飾子(/の後ろで指定するオプション)について、実際のコードで学ぶ。
<?php
$str='Hi, I am Taro.';
$result=preg_match('/taro/i',$str);
var_dump($result);
/taro/iの部分が正規表現となる。「i」は「アルファベットの大文字小文字を区別しない」ことを指定するパターン修飾子。このコードではvar_dump($result)で1を返すが、iを外すと0を返すようになる。
パターン修飾子は以下の通り。
i...アルファベットの大文字小文字を区別しない
例:/abc/i→「ABC」「aBc」などにマッチする
m...行単位でマッチングを行う
例:/abc/m→複数行のすべての行頭を調べる
s...パターン構文の「.(ドット)」を改行文字にもマッチさせる
例:/abc/s→任意の1文字が改行文字であってもマッチする
u...パターン文字列の文字コードを「UTF-8」として扱う
例:/テニス/u→日本語環境でpreg_match()をする場合は必須
x...パターン中の空白文字を無視する
例:/ab c/x→「abc」にマッチする。「ab c」にはマッチしない。
正規構文の終わりに指定するのがパターン修飾子。
mについてはイメージしづらいので実際のコードで確認。
<?php
$sentences=<<<EOD
はじめまして。
私の名前は田中です。
休日はジョギングをしています。
EOD;
$result=preg_match('/^休日/um',$sentences);
var_dump($result);
この場合、var_dump($result)は1を返す。mを削除すると、var_dump($result)は0を返す。
第9章-02 練習問題
- 数字4文字の文字列にマッチする正規表現を作り、サンプルの文字列を調べよ。
- 行の終端が「でしょう。」になっている文を、例示したコードを使って探せ。
<?php
//1.解答
$str1='0120';
$str2='090';
$result1=preg_match('/[0-9]{4}/',$str1);
$result2=preg_match('/[0-9]{4}/',$str2);
var_dump($result1);
var_dump($result2);
//2.解答
$str1='今日はくもりです。';
$str2='明日は快晴でしょう。';
$result1=preg_match('/\Sでしょう。/u',$str1);
$result2=preg_match('/\Sでしょう。/u',$str2);
var_dump($result1);
var_dump($result2);
第9章-03
いくつかの例で正規表現を作っていく。
まずは携帯の番号でバリデーションをかけると想定。
- ハイフンを入れる/入れないは人それぞれ
- 最初の3桁は「070」「080」「090」で固定
最初の3桁は0と0の間に7から9までのいずれかの数字が入るので、
0[7-9]0
となる。次はハイフンが入った後に数字4桁。数字には「\d」という書き方もあるので、
0[7-9]0-\d{4}-\d{4}
携帯番号専用のフォームならば、前後に余分な文字はないはずなので始まりと終わりを表す「^」と「$」を付け加える。また、ハイフンがない場合も想定して「?(0回か1回繰り返す)」をハイフンの後ろに書いておく。
^0[7-9]0-?\d{4}-?\d{4}$
これがしっかり機能するか、実際のコードで確認する。
<?php
$num1='090-1234-5678';
$num2='08012345678';
$num3='070-1234-567';
$pattern='/^0[7-9]0-?\d{4}-?\d{4}$/u'; // 正規表現の変数に格納
$result1=preg_match($pattern,$num1);
$result2=preg_match($pattern,$num2);
$result3=preg_match($pattern,$num3);
var_dump($result1);
var_dump($result2);
var_dump($result3);
画面では$result3のみ0を返すので、しっかり機能していることが分かる。
次に、西暦の記述を見つけるための正規表現を練習する。
「2018/07/03」のように、「/」など既にパターン上で意味を持ってしまっている特殊文字について扱う。特殊文字は「/」をはじめ「.」「?」「*」「$」「[」「)」など様々あるが、ただの文字として表現したい場合はそのまま書くわけにはいかない。特殊文字のエスケープについて学んでいく。
西暦は「数字4つ」「/」「数字2つ」「/」「数字2つ」で表す。また、月日については1桁で表すこともあるので、まずは以下のように書く。
/^\d{4}/\d{1,2}/\d{1,2}$/u
コードの最初と最後の/はpreg_matchに必須の文字であり、「デリミタ」という名前がついている。「独立した領域の境界を特定する文字」として使われている。正規表現はPHP以外の言語でも使えるので、PHPプログラムの中に正規表現のための独立した領域が作られていることになる。
正規表現の中に出てくる/はただの文字としてのスラッシュなので、バックスラッシュ「\」でエスケープする必要がある。
/^\d{4}\/\d{1,2}\/\d{1,2}$/u
文字としての/の前に\を付け加え、「\/」と書くことによってデリミタとして判断されてしまうことを回避している。正規表現中で構文となっている文字をただの文字として使うにはすべてエスケープしなければならない点に注意する。
見づらくなってしまったので、( )で囲ってグループ化を行う。
/^(\d{4})\/(\d{1,2})\/(\d{1,2})$/u
丸かっこがなくても動作はするが、この方が見やすい。
ここまで作った表現をコードで動かす。
<?php
$date1='2018/7/2';
$date2='2018/10/20';
$date3='2018年7月2日';
$pattern='/^(\d{4})\/(\d{1,2})\/(\d{1,2})$/u';
$result1=preg_match($pattern,$date1);
$result2=preg_match($pattern,$date2);
$result3=preg_match($pattern,$date3);
var_dump($result1);
var_dump($result2);
var_dump($result3);
var_dump($result3)のみ0を返し、他は1を返したので成功。
今夜はここまで。