regular expressions
正規表現

注意事項

毎回の授業の最初に出席メールを提出せよ.

前回までの出席記録を確認せよ.出席メールを授業時間内に 送信したのに記載されていない学生はメールの自分宛のコピーを印刷し,宛先アドレス, 提出時間が間違っていないことを確認してから申し出よ.

4 章 つづき

4.9 POSIX 形式の正規表現

ロケールとはどの言語を主として扱っているかをシステムに知らせる仕組みである. POSIX 正規表現は設定されているロケールに合わせて適当な処理を行うことが利点である. 反面,POSIX 正規表現はテキストデータのみしか扱えない.NUL 文字等,文字コード外の データは扱えないのでそれが必要なときは Perl の正規表現を使う..

表 4.7 の文字クラスの展開は設定されているロケールの言語に合わせて展開される.

ereg('y(.*)e$', 'Sylvie', $a); のパターンは,「yのあと任意の文字列, 最後に e で行末に達する」という意味になる.これを実行するとパターン全体の 'y(.*)e$' は 'ylvie' にマッチする. サブパターンの'(.*)' 部分は 'lvi' にマッチしている.マッチした結果が第三引数の配列に返される. この場合は $a[0](マッチした文字列部分全体) に 'ylvie', $a[1](最初のサブパターン) に 'lvi' が格納される.

PHP スクリプトの外枠 を利用して以下の例のコードを sample06.php という PHP スクリプトとして完成させ,動作確認を行え.

例 6

ereg('y(.*)e$', 'Sylvie', $a);
var_dump($a);

マッチするか否かの判定だけでなく,ereg_replace() で文字列の置換を行うことができる. ことのき,パターンに括弧()をいくつか入れてサブパターンを作っておくと, \1 から \9 までを使ってサブパターンにマッチした内容を後で使うことができる.

例えば [b] と [/b] で囲まれた部分があれば,その囲まれた内容はそのままで <b></b> で囲まれたものに変換したい場合を考える. 'It is [b]not[/b] a matter of diplomacy.' という文字列が入力されれば 'It is <b>not</b> a matter of diplomacy.' に変換したい. [b] と [/b] で囲まれた文字列を言葉で表すと「"["以外の文字が任意個」であり, 「"["以外の文字一個」の正規表現は「[^[]」である.故に「"["以外の文字が任意個」に対応する 正規表現は「[^[]*」であるからこれを括弧で括ってサブパターンとして全体の中に入れる. 結局全体の正規表現はエスケープシーケンスを考慮して 「\[b]([^[]*)\[/b]」となる. 置換後の文字列としてサブパターン「([^[]*)」にマッチしていた系列を呼び出すには \1 を利用すればよい.

教科書 p. 113 下にある例のコードを sample07.php という PHP スクリプトとして完成させ,動作確認を行え.

例 7

$string = 'It is [b]not[/b] a matter of diplomacy.';
echo ereg_replace('\[b]([^[]*)\[/b]', '<b>\1</b>', $string);

eregi_replace() は大文字小文字の区別をしない.

sprit() で長い文字列を分割して配列に格納できる.分割するときの セパレータを正規表現で指定する.

4.10 Perl 互換の正規表現

例題 2

学生証番号にマッチする Perl の正規表現を考えたい. 学生証番号の規則は「一文字目が数字,2 文字めが A,B,K,L のどれか, 3,4 文字目が大文字の英字,5-8 文字が数字」とし,学生証番号 にマッチするパターンを考えよ.

PHP スクリプトの外枠 にパターンを記入した以下のコードを挿入せよ. ex02.php というファイル名で スクリプトを作成し,学生証番号である文字列,ない文字列 についていくつかテスト実行を行い,動作を確認せよ.

例題 2

if(preg_match('/「ここにパターンを記入」/', "0BJT1234")){
  echo "matched";
} else {
  echo "not matched";
}

Perl の正規表現ではパターンの両端をデリミタで囲む.一般的には「/」が使われるが, パス名などパターン内に 「/」が頻出するような例ではいちいちバックスラッシュで エスケープする必要がある.下の例では「/usr/local/」というパターンを指定したいが デリミタが標準の「/」であるため,パターン内のスラッシュすべてにエスケープの ためのバックスラッシュをつける必要がある.

preg_match('/\/usr\/local\//', '/usr/local/bin/perl');

このような例ではスラッシュ以外,例えば「#」をデリミタに指定するとパターン内で スラッシュ記号が使えるようになる

preg_match('#/usr/local/#', '/usr/local/bin/perl');

後置オプションでパターンマッチ動作の振る舞いを変更することができる.

以下の例のコード(教科書p.115)を sample08.php という PHP スクリプトとして完成させよ. $captured の内容がどうなるか予想し,確認せよ.

例 8

preg_match('/is (.*)$/', "the key is in my pants", $captured);
var_dump($captured);

preg_match では POSIX 正規表現に加えて表 4-9 (p.115) の文字クラスが使える.

表 4-10 (p.116) のアンカーが使える.行頭,行末アンカーがよく使われる.

「.*」は任意の文字の任意回数の繰り返しだから,パターン「<.*>」は 「<b>」にも「<b>not</b>」にもマッチする.POSIX も Perl も標準ではマッチする限り最長のパターンにマッチさせるという原則がある.

以下の例のコード(教科書p.116)を sample09.php という PHP スクリプトとして完成させよ. 「<b>」ではなく「<b>not</b>」にもマッチしていることを確認せよ.

この例では $match[1] の値は "<b>not</b>" であるがブラウザにはそう表示されない.ソースを表示し,理由を考えよ.

例 9

preg_match('/(<.*>)/', 'do <b>not</b> press the button', $match);
echo "$match[1]";

量指定子「*」の代わりに「*?」を使うと,マッチするうち最も小さいものにマッチする. 例 9 の量指定子を「*?」に変更し,確認せよ.ここでもソースを表示しないと確認できない ことに注意せよ.

正規表現のパターン中に特に必要ないように見える括弧が書かれてているときは サブパターンにマッチした結果を使っている場合がある. パターン「[[:alpha:]]+」は1文字以上の英文字列である. 「[[:alpha:]]+\s+[[:alpha:]]+」で「英文字系列があり,一文字以上の空白系列 のあとに英文字系列」というパターンである.この場合は最初の英文字系列と後の 英文字系列には関係はない.最初の英文字系列に括弧をつけるとそこで実際にマッチした 系列に「\1」という名前でアクセスできる. 「([[:alpha:]]+)\s+\1」というパターンは「英文字系列があり, 一文字以上の空白系列のあとに英文字系列」というパターンのうち,後の英文字系列が 最初の英文字系列と同一のものにのみマッチする.

例えば「abc def」は「[[:alpha:]]+\s+[[:alpha:]]+」にはマッチするが 「([[:alpha:]]+)\s+\1」にはマッチしない.「abc abc」であれば両方にマッチする.

Perl 由来の後置オプションが使える(p. 117,表4-12)大文字/小文字を区別 しないマッチを行う i オプションはよく使われる.s オプションをつけることで 「.」が改行文字にマッチするようになる.デフォルトでは「.」が改行文字にマッチ しないことに注意する必要がある.

「インラインオプション」でパターンの一部だけにオプションを効かせることができる.

p.119 から p.200 の例はシングルクォートとシングルクォートで挟まれた文字列 をキャプチャするものであるが,前にバックスラッシュのついたシングルクォートは 通常文字として文字列の切れ目とは扱わないというパターンである.パターンとして 設定されるヒアドキュメント自体が一種の引用符なので「\」を記述するために「\\」と書く必 用がある.ところがこの文字列自身マッチ文字列として渡されるのでこの時点でも 「\\」→「\」の置き換えが行われる.このため,シングルクォートの直前の一個の 「\」を記述するのに 4 個のバックスラッシュが必要になる.

正規表現と後方参照は文字列の置換で大きな力を発揮する. 以下の例で文字列置換を試してみよ.

例 10

$new = preg_replace('/Hiratsuka/', 'Takanawa','My university is in Hiratsuka');
echo "$new";

preg_replace の第 3 引数に文字列の配列を渡すこともできる. その場合,配列の文字列すべてについて置換が行われる.

パターンを複数渡す事もできる.

文字列置換時には後方参照は有用である.以下の例の動作を確認せよ.

例 11 (p.124)

echo preg_replace('/(\w)\w+\s+(\w+)/', '$2, $1.', 'Fread Flintstone');

例 11 のパターンは以下のようにマッチングしている.

文字列 F read (空白記号) Flintstone
パターン (\w) \w+ \s+ (\w+)
後方参照 $1 $2

後方参照を利用して '$2, $1.' に書き換えるため,結果は「Flintstone, F.」となる.

preg_split で正規表現で表現された区切り文字で文字列を分解し, 配列に格納することができる.(p.125)

preg_grep は配列要素のうちパターンにマッチするものだけを返す.

preg_quote は正規表現で特別な意味をもつ文字をエスケープする処理を行う.

第 4 回課題「正規表現」 を提出せよ.


Updated in May 18, 2010, index.html, Yamamoto Hiroshi