4

ダブルエスケープが必要な場合と必要でない場合のしっかりした記事を探していましたが、何も見つかりませんでした。どこかに説明があると思うので、たぶん私は十分に一生懸命に見えませんでしたが、この質問をしている次の人を簡単に見つけられるようにしましょう!

たとえば、次の正規表現パターンを考えてみましょう。

/\n/
/domain\.com/
/myfeet \$ your feet/

画期的なことは何もありませんか?OK、PHPのpreg_match関数のコンテキスト内でこれらの例を使用しましょう。

$foo = preg_match("/\n/", $bar);
$foo = preg_match("/domain\.com/", $bar);
$foo = preg_match("/myfeet \$ your feet/", $bar);

私の理解では、引用符で囲まれた文字列値のコンテキストでの円記号は次の文字をエスケープし、式は引用符で囲まれた文字列値を介して与えられます。

前は次のようになりますか?これはエラーを引き起こしませんか?:

$foo = preg_match("/n/", $bar);
$foo = preg_match("/domain.com/", $bar);
$foo = preg_match("/myfeet $ your feet/", $bar);

どちらが私が正しく望んでいないのですか?これらの式は上記と同じではありません。

このようにダブルエスケープして書く必要はないでしょうか?

$foo = preg_match("/\\n/", $bar);
$foo = preg_match("/domain\\.com/", $bar);
$foo = preg_match("/myfeet \\$ your feet/", $bar);

PHPが文字列を処理するときに、バックスラッシュをバックスラッシュにエスケープし、PCREインタープリターに渡されるときにバックスラッシュが残されるようにしますか?

\"または、PHPは、そのバックスラッシュをPCREインタープリターに渡したいことを魔法のように知っていますか?つまり、式で使用したい引用符をエスケープしようとしていないことをどうやって知るのでしょうか?または、エスケープされた引用符を使用する場合、ダブルスラッシュのみが必要ですか?そして、そのことについては、引用符をトリプルエスケープする必要がありますか?\\\"引用がエスケープされ、ダブルが残っているように、あなたは知っていますか?

これの経験則は何ですか?

PHPでテストを行いました。

$bar = "asdfasdf a\"ONE\"sfda dsf adsf me & mine adsf asdf asfd ";

echo preg_match("/me \$ mine/", $bar);
echo "<br /><br />";
echo preg_match("/me \\$ mine/", $bar);
echo "<br /><br />";
echo preg_match("/a\"ONE\"/", $bar);
echo "<br /><br />";
echo preg_match("/a\\\"ONE\\\"/", $bar);
echo "<br /><br />";

出力:

0

1

1

1

ですから、どういうわけか引用符では問題ないように見えますが、ドル記号の場合は、思ったとおりダブルエスケープが必要です。

4

5 に答える 5

8

二重引用符で囲まれた文字列

二重引用符で囲む場合、PHPは円記号の直後の文字を検査するという規則があります。

隣接する文字がセットに含まれているntrvef\$"場合、またはそれに続く数値の場合(ルールはここにあります)、対応する制御文字または順序(16進数または8進数)表現としてそれぞれ評価されます。

無効なエスケープシーケンスが指定された場合、式は評価されず、円記号と文字の両方が残ることに注意することが重要です。これは、無効なエスケープシーケンスが代わりにエラーを引き起こす他のいくつかの言語とは異なります。

たとえば"domain\.com"、そのままにしておきます。

変数は二重引用符で囲まれて展開されることに注意してください。たとえば、"$var"としてエスケープする必要があります"\$var"

一重引用符の文字列

PHP 5.1.1以降、一重引用符で囲まれた文字列(およびその後に少なくとも1文字)内の円記号はそのまま出力され、変数も置換されません。これは、一重引用符で囲まれた文字列の最も便利な機能です。

正規表現

正規表現をエスケープするには、エスケープをpreg_quote()次のように残すのが最善です。

$foo = preg_match('/' . preg_quote('mine & yours', '/') . '/', $bar);

このように、どの文字をエスケープする必要があるかを心配する必要がないので、ユーザー入力に適しています。

参照:preg_quote

アップデート

このテストを追加しました:

"/me \$ mine/"

これは"/me $ mine/";として評価されます。ただし、PCREでは$特別な意味があります(これはサブジェクトの終わりのアンカーです)。

"/me \\$ mine/"

これはとして評価される"/me \$ mine/"ため、バックスラッシュはPHP自体ではエスケープされますが$、PCREではエスケープされます。これは偶然にのみ機能します。

$var = 'something';

"/me \\$var mine/"

これはとして評価される"/me \something"ので、もう一度エスケープする必要があります$

"/me \\\$var mine/"
于 2013-02-09T00:10:09.760 に答える
1

一重引用符を使用します。それらはエスケープシーケンスの発生を防ぎます。

例えば:

php > print "hi\n";
hi
php > print 'hi\n';
hi\nphp > 
于 2013-02-09T00:09:55.097 に答える
0

無効なエスケープシーケンスがある場合は常に、PHPは実際には文字列に文字を残します。ドキュメントから:

一重引用符で囲まれた文字列と同様に、他の文字をエスケープすると、円記号も印刷されます。

つまり"\&"、実際にはとして解釈され"\&"ます。エスケープシーケンスはそれほど多くないので、ほとんどの場合、1つの円記号で逃げることができます。ただし、一貫性を保つために、円記号をエスケープすることをお勧めします。

いつものように:あなたが何をしているのかを知ってください:)

于 2013-02-09T00:16:14.223 に答える
0

OKそれで、私はさらにいくつかのテストを行い、PCREを二重引用符でカプセル化するときに経験則を発見しました。次のことが当てはまります。

$-テキストが直後に続く場合、PHPはそれを変数の先頭として解釈するため、二重エスケープが必要です。エスケープせずに放置すると、針の終わりを示し、壊れます。

\r\n\t\v-特別なPHP文字列エスケープ。単一のエスケープのみが必要です。

[\^$.|?*+()-特殊な正規表現文字。単一のエスケープのみが必要です。ダブルエスケープは、不必要に使用されても式を壊さないようです。

"-カプセル化のため、引用符は明らかにエスケープする必要がありますが、エスケープする必要があるのは1回だけです。

\-バックスラッシュをお探しですか?式の二重引用符によるカプセル化を使用すると、3回のエスケープが必要になります。\\(合計4つの円記号)

足りないものはありますか?

于 2013-02-09T00:47:27.057 に答える
0

以下に書くのは正確には何が起こるかではないということから始めますが、わかりやすくするために、簡単にします。

正規表現を使用するときに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は、文字列内の改行と「魔法のように」一致します。ここでエスケープシーケンスを確認してください:エスケープシーケンス

最初に言ったように、物事は私がここで説明しようとしたものとまったく同じではありませんが、それが役立つことを本当に望んでいます(そしてそれがすでにあるよりも混乱させないでください)。

于 2018-02-16T02:49:27.010 に答える