以下に書くのは正確には何が起こるかではないということから始めますが、わかりやすくするために、簡単にします。
正規表現を使用するときに2つの評価が行われると想像してください。1つ目はPHPによって行われ、2つ目はPCREによって行われ、まるで別々のエンジンであるかのようになります。そして私たちの不運のために、
PHPとPCREは、さまざまな方法で物事を評価します。
ここには3人の「男」がいます:1)ユーザー。2)PHPおよび; 3)PCRE。
USERは、コードエディタに入力したコードとまったく同じコードを記述してPHPと通信します。次に、PHPはこのコードを評価し、別の情報をPCREに送信します。この情報は、コードに入力した情報とは異なります。次に、PCREはそれを評価してPHPに何かを返し、PHPはこの応答を評価してUSERに何かを返します。
以下の例で詳しく説明します。そこで、バックスラッシュ( "\")を使用して何が起こっているのかを説明します。
phpファイルの次のコードを想定します。
<?php
$sub = "A backslash \ in a string";
$pat1 = "#\#";
$pat2 = "#\\#";
$pat3 = "#\\\#";
$pat4 = "#\\\\#";
echo "sub: ".$sub;
echo "\n\n";
echo "pat1: ".$pat1;
echo "\n";
echo "pat2: ".$pat2;
echo "\n";
echo "pat3: ".$pat3;
echo "\n";
echo "pat4: ".$pat4;
?>
これは印刷されます:
sub: A backslash \ in a string
pat1: #\#
pat2: #\#
pat3: #\\#
pat4: #\\#
この例では、正規表現は含まれていないため、発生しているコードのPHP評価のみが行われます。
PHPは、特殊文字の前にない場合、バックスラッシュをそのまま残します。これが、バックスラッシュを$subに正しく出力する理由です。
PHPは$pat1と$pat2をまったく同じように評価します。これは、$ pat1ではバックスラッシュがそのまま残され、$ pat2では最初のバックスラッシュが2番目のバックスラッシュをエスケープして、単一のバックスラッシュになるためです。
ここで、$ pat3では、最初の円記号が2番目の円記号をエスケープし、1つの円記号になります。次に、PHPは3番目の円記号を評価し、特別なものの前にないため、そのままにします。結果は二重の円記号になります。
これで、誰かが「しかし、今度は2つのバックスラッシュがあります!最初のバックスラッシュが2番目のバックスラッシュから逃げるべきではありませんか?!」と言うことができます。答えはいいえだ"。PHPが最初の2つの円記号を1つの円記号に評価した後、PHPは再び振り返ることはなく、次の円記号の評価を続けます。
この時点で、$ pat4で何が起こっているかはすでにわかっています。最初の円記号は2番目の円記号をエスケープし、3番目の円記号は4番目の円記号をエスケープして、最後に2つを残します。
PHPがこれらの文字列に対して何をしているのかが明確になったので、前のコードの後にさらにコードを追加しましょう。
if (preg_match($pat1, $sub)) echo "test1: true"; else echo "test1: false";
echo "\n";
if (preg_match($pat2, $sub)) echo "test2: true"; else echo "test2: false";
echo "\n";
if (preg_match($pat3, $sub)) echo "test3: true"; else echo "test3: false";
echo "\n";
if (preg_match($pat4, $sub)) echo "test4: true"; else echo "test4: false";
そして結果は次のとおりです。
test1: false
test2: false
test3: true
test4: true
したがって、ここで起こっていることは、PHPがCODEに「入力した内容」を直接PCREに送信していないということです。代わりに、PHPは以前に評価したものを送信しています(これはまさに上記で見たものです)。
test1とtest2の場合、テストごとにCODEに異なるパターンを記述していても、PHPは同じパターン#\#をPCREに送信しています。test3とtest4でも同じことが起こります。PHPは#\\#を送信しています。したがって、test1とtest2の結果は、test3とtest4の結果と同じです。
さて、PCREがこれらのパターンを評価するとどうなりますか?PCREはPHPのようには機能しません。
test1とtest2では、PCREが単一のバックスラッシュが特別なもの(またはまったくないもの)をエスケープしていることを確認すると、そのままにはなりません。代わりに、おそらく「これは一体何なのか」と考えています。そして、エラーをPHPに返します(実際には、単一のバックスラッシュをPCREに送信し、これを検索したときに何が起こっているのかはわかりませんが、それでも決定的なものではありません)。次に、PHPは、エラーであると想定しているものを「false」と評価し、それを残りのCODE(この例ではif()関数)に返します。
test3とtest4では、現在の予想どおりに動作します。PCREは、最初のバックスラッシュを2番目のバックスラッシュをエスケープしていると評価し、単一のバックスラッシュを生成します。もちろん、これは$ sub文字列と一致し、「成功したメッセージ」をPHPに返します。PHPはそれを「true」と評価します。
質問
への回答
一部の文字はPHPに固有のものです(たとえば、改行の場合はn、TABの場合はt )。
一部の文字はPCREに固有です(たとえば、任意の文字に一致する場合は。(ドット)、空白に一致する場合はs )。
また、一部の文字は両方に固有です(たとえば、 $ to phpは変数名の先頭であり、PCREでは件名の末尾をアサートします)。
そのため、このように改行を1回だけエスケープする必要があります\n。PHPはそれをREAL文字のNEWLINEとして評価し、PCREに送信します。
ドットの場合、その特定の文字に一致させる場合は、\を使用する必要があります。ドットは文字列内のPHPの特殊文字ではないため、PHPは何もしません。代わりに、それらをそのままPCREに送信します。PCREでは、ドットの前に円記号が「表示」され、その特定の文字と一致する必要があることを理解します。ダブルエスケープ\\を使用する場合。最初の円記号は2番目の円記号をエスケープし、同じ結果を残します。
また、文字列のドル記号と一致させる場合は、\\\$を使用する必要があります。PHPでは、最初の円記号は2番目の円記号をエスケープし、1つの円記号を残します。次に、3番目の円記号はドル記号をエスケープします。最終的に、結果は\$になります。これはPCREが受け取るものです。PCREはそのバックスラッシュを確認し、ドル記号が件名の終わりを表明しているのではなく、リテラル文字を表明していることを理解します。
見積もり
そして今、私たちは引用に来ました。それらの問題は、PHPが文字列を囲むために使用される引用符に応じて、さまざまな方法で文字列を評価するという事実です。それをチェックしてください:文字列
この時点まで私が言ったことはすべて、二重引用符に有効です。この'\n'を一重引用符で囲むと、PHPはその円記号をリテラルとして評価します。
ただし、正規表現で使用されている場合、PCREはこの文字列をそのまま取得します。また、nはPCREに固有であるため、改行文字として解釈され、BOOMは、文字列内の改行と「魔法のように」一致します。ここでエスケープシーケンスを確認してください:エスケープシーケンス
最初に言ったように、物事は私がここで説明しようとしたものとまったく同じではありませんが、それが役立つことを本当に望んでいます(そしてそれがすでにあるよりも混乱させないでください)。