PHPプロ!TIPS+

preg_quoteで特殊文字をエスケープ

preg系関数は非常に便利で、perl互換の正規表現が使えます。 ユーザーの入力内容のバリデートや文章の整形など、使いかたは様々です。

便利なpreg系関数ですが、検索するパターンに正規表現で用いる特殊文字を使いたい場合は「\」(バックスラッシュまたは円記号)を付けてエスケープする必要があります。

ですが、「ユーザーからの入力値をパターンにしたい時」のように動的に値が変わる場合は、手動でエスケープすることはできませんし、「プログラムの構文を整形したい時」「ファイル名の置換をしたい時」などの場合は、エスケープする文字が多くなりソースコードが読みにくくなってしまいます。

もちろん、ループ文でエスケープする処理を書くこともできますが、preg系関数には特殊文字をエスケープする関数「preg_quote」があるので簡単にエスケープできます。

preg_quoteがエスケープする文字は、

. \ + * ? [ ^ ] $ ( ) { } = ! < > | :

です。preg系関数で用いる特殊文字は一通りエスケープすることができるのですが、"/"はエスケープしてくれません。(これのエスケープ方法は後述します)

例えば、「George is man?」を「George is man.」に置換したいという場合を考えてみます。

検索パターン($pat)には「man?」を指定して、置換する文字列($rpl)には「man.」を指定しています。

$txt = "George is man?";
$pat = "/man?/";
$rpl = "man.";
$str = preg_replace($pat, $rpl, $txt);

この場合、予想通りに動作するようにも見えますが、preg_replaceの第一引数は 「/man?/」なので、「ma」、「man」にマッチしてしまい、結果は「man.?」になってしまいます。

予想通りに動作させるには、検索パターンの「man?」を「man\?」にする必要があります。この処理を自動的にやってくれるのが、preg_quoteです。

$txt = "George is man?";
$pat = "/man?/";
$rpl = "man.";
$str = preg_replace(preg_quote($pat), $rpl, $txt); //preg_quoteを使用

のようにすれば、「?」をエスケープして「man\?」となり、「man?」にマッチするので無事に「Gorge is man.」に置換できます。

便利なpreg_quoteですが、上記の例ではデリミタである"/"は自動的にはエスケープしないので"http://www"のようなものには使えません。このような場合は、preg_replaceの第二引数を使用します。

第二引数にはデリミタとして使用するエスケープしたい任意の文字列を指定できるので、ここで"/"を指定すれば、

$txt = "http://example.com/main/pictures/";
$pat = "http://example.com/";
$rpl = "/var/www/";
$str = preg_replace("/" . preg_quote($pat, "/") . "/", $rpl, $txt);

のようなコードでも正常に動作させることができます。

このpreg_quoteは、プログラムを予想通りに動作させること以外に、

//preg_quote非使用
$pat =
"//\.\.\/hoge\/piyo-fuga\/\[07-02-02\]\(subtitle\)backup\.tar\.bz2//";

//preg_quote使用
$pat = "/" .
preg_quote("../hoge/piyo-fuga/[07-02-02](subtitle)backup.tar.bz2", "/")
. "/";

このように、エスケープすると見づらくなるソースもわかりやすくなります。

ひとつエスケープを忘れたのに気づかずに誤動作を起こして、デバッグに時間が かかってしまうなどのリスクを軽減できます。

ループ文を使用してエスケープ処理を書くより、この関数を使ったほうが無駄な手間を省いてスマートに記述できます。

もし、preg系関数のエスケープ処理を書く機会があれば、使ってみてはいかがでしょうか。

バックナンバーについて

TIPS-MLは、毎週金曜日に更新され、新しい記事が掲載されます。

Tipsꗗy[W 

Pick Up Q&A

Q
ログファイルの中の空のデータ行を削除したい
 このエントリーをはてなブックマークに追加 
A
ログのデータ個数(列数)が固定で、空のログが"<><><>"だと既知であれば if ($line === "<><><>") { continue; } で読み飛ばしてもいいのでは? ...

>>続きを読む

まずは配列や文字列の扱いから、じっくり勉強して行きましょう。

▲解説者:岡本(アシアル株式会社 教育コーディネーター兼 システムエンジニア)

Q&A掲示板 新着情報