【PHP】正規表現を用いて文字列を操作する

PHP で正規表現を使う場合、以下の PCRE関数 が用意されているので目的に応じて使い分ける。


関数名説明
preg_filter正規表現による検索と置換
preg_grepパターンにマッチする配列の要素を返す
preg_last_error_msg最後に実行した PCRE正規表現に関するエラーメッセージを返す
preg_last_error最後に実行した PCRE正規表現のエラーコードを返す
preg_match_all繰り返し正規表現で検索する
preg_match正規表現による検索を行う
preg_quote正規表現文字をクオート(引用)する。
preg_replace_callback_array正規表現検索をしてコールバック関数を使用して置換する
preg_replace_callback同上
preg_replace正規表現で検索と置換をする
preg_split正規表現で文字列を分割する

今回は昨日投稿した 【PHP】オレオレ backtrace ログを出力して呼出元を辿るdebug_backtrace のフルパスのファイル名を preg_replace を使って短くしてみようと思う。
preg_replace の定義はこちら。

そのままだけど 第1引数で正規表現のパターン、第2引数で置換する文字列、第3引数で検索対象となる文字列、第4引数(オプション)で置換する最大回数、第5引数(オプション)で置換回数をカウントする。主に利用するのは第3引数までだろう。

今回対象とする文字列はこんな感じ。

正規表現で表すためにはまず対象の文字列をよく見る。対象文字列はフルパスになるので、先頭は常に / となるはず。そして、末尾も同様に .php で終わるはず。その間の文字列が色々変わるはず。

(PHP に限らず、正規表現のパターンの書き方はほぼ統一されているので 【正規表現 書き方】あたりで検索すると見やすいチートシートがたくさん見つかると思う。)

正規表現ではパターンを表すのに / で囲う。そして文字列の先頭を意味するのが ^ なので、まずはこうしてみる。

これで先頭から任意の文字(.)の繰り返し(*)ということで対象文字列全てをマッチさせることができる。
続いて、フルパスだとどうしても / を使うのだけど、正規表現パターンで / は特殊な意味を持つため、 / を特殊文字ではなく、普通の文字としての / だと明示しないといけない(これをエスケープするという)。エスケープは対象文字の前に \\ をつける。

これで先頭が / で始まり、後に続く文字はなんでもマッチする正規表現となった。先頭からの文字はこれ以上一般化できないので、次に考えるのは末尾。末尾を意味する文字は $ 。
末尾は上述のように .php で終わるので . をエスケープして \\.php となる。
それからファイルが格納されているディレクトリを意味する / があるはずなので以下のようになるはず。

ディレクトリ名とファイル名はなんでも良いので、任意の文字列を意味する .* としたい( . も * も特殊文字)。

だけど、これだと .* に全ての文字列がマッチしてしまって、上記のフルパスだと全部マッチしてしまう。そこで、最短マッチとして .*? と書く。こうすると色々なパターンでマッチする中で最短のものを選ぶようになる。
以上を組み合わせるとこんな正規表現が出来上がった。

で、これを preg_replace に渡すんだけど、preg_replace はこれだと対象文字列を全てマッチさせるのでどの部分をどう置き換えればいいのかわからない。そこで、() で囲ってここが対象ブロックだよと教えてあげる。

2つの () で囲った。これで preg_replace では 1つ目の () を $1 で参照することができ、2つ目の () を $2 で参照することができるようになった。今回は 1つ目の () を消して 2つ目の () だけにしたいので最終的にこうなる。

preg_replace の第2引数が置換後の文字列となる。今回は全ての文字列をマッチさせた上で $1 を捨てて ‘$2’ とすることで /ディレクトリ/ファイル.php だけが残るという仕組み。もしも /ディレクトリ の / が不要なら

としてもいいかもしれない。わかりやすく説明文をつけるなら $2 の前に固定の文字列を入れることもできる。

正規表現のパターンはいくらでもあると思うのでもっとスマートな方法がないか模索してもいいと思います。

カテゴリー: PHP