これを試してみてください:
説明は、コードの後とコードの本体内の両方で、コメントとして提供されます。
<?php
class String
{
private $str;
public function __construct($str)
{
$this->str=$str;
}
public function replace($regex,$replacement)
{
return preg_replace($regex,$replacement,$this->str);
}
}
function String($str)
{
return new String($str);
}
echo String('A')->replace('/^[^\pL]*|[^\pL]*$/','*').'<br />';//Outputs *A*
//Why does this output *A* and not A?
//Because it successfully matches an empty string
//The easiest way to test for the presence of an empty string is like so:
echo String('A')->replace('//','*').'<br />';//Outputs *A*
//The engine begins by placing its internal pointer before the string like so:
// A
//^
//It then tests the regular expression for the empty string ""
//Most regular expressions will fail this test. But in our case matches it successfully.
//Since we are preforming a search and replace the "" will get replaced by a "*" character
//Then the internal pointer advances to the next character after its successful match
// A
// ^
//It tests our regular expression for the A character and it fails.
//Since we are performing a search and replace the searched "A" portion remains unchanged as "A"
//The internal pointer advances to the next character
// A
// ^
//It tests our regular expression for the empty string ""
//Again, most regular expressions will fail this test. But since ours successfully matched it,
//The "" portion will get replaced by "*"
//The engine then returns our output:
//*A*
echo '<hr />';
//If we wanted to replace the A character too, we'd do this:
echo String('A')->replace('/|A/','*').'<br />';//Outputs ***
//Or we could do:
echo String('A')->replace('/.*?/','*').'<br />';//Outputs ***
//Thus we see for a 1 character string the engine will test for the empty spaces "" before and after the character as well
//For a 19 character string it tests for all the gaps between each character like so:
echo String('19 character string')->replace('//','*').'<br />';//Outputs *1*9* *c*h*a*r*a*c*t*e*r* *s*t*r*i*n*g*
//For an empty string it would match once successfully like so:
echo String('')->replace('//','*').'<br />';//Outputs *
echo String('A')->replace('/^[^\pL]*|[^\pL]*$/','*');//Outputs *A*
上記の出力ではなく、なぜ出力*A*
されないのA
ですか?
この正規表現は、空の文字列に対して正常に""
一致するためです。
次のように、空の正規表現を使用しても同じ動作が観察されます。ここで、正規表現エンジンの実装がこれらの奇妙な結果を生成する理由
echo String('A')->replace('//','*');//Outputs *A*
を説明します。 後になって、それらがまったく変わったものではなく、実際には正しい動作であることがわかるでしょう。
エンジンは、次のように文字列の前に内部ポインターを配置することから始めます。
A
_ _ _
^
ポインターは空の文字列を指しているため、""
正規表現に対してテストします。
正規表現を満たすために必要な最小文字数は通常 1 つ以上であるため、ほとんどの正規表現はこのテストに失敗します。しかし、この例では、正規表現に対して 0 文字が有効な一致であるため、一致は成功しています。
検索と置換を実行しているため""
、文字に置き換えられ"*"
ます。
次に、一致が成功した後、内部ポインターは次の文字に進みます。
A
_ _ _
^
"A"
文字の正規表現をテストし、失敗します。
検索と置換を実行しているため、検索された"A"
部分は変更されません"A"
。内部ポインタは次の文字に進みます:
A
_ _ _
^
空の文字列の正規表現をテストし""
ます。繰り返しますが、ほとんどの正規表現はこのテストに失敗します。
しかし、正規表現が正常に一致するため、その""
部分は次のように置き換えられます"*"
。その後、エンジンは文字列のループを終了し、出力"A"
を返します。"*A*"
A 文字も置き換えたい場合は、次のようにします:
echo String('A')->replace('/|A/','*');//Outputs ***
または、次のようにすることもできます:
echo String('A')->replace('/.*?/','*').'<br />';//Outputs ***
したがって、1 文字の文字列の場合、エンジンは文字の""
前後もテストすることがわかります。
19 文字の文字列の場合、次のように各文字間のすべてのギャップをテストします。
echo String('19 character string')->replace('//','*');
//Outputs *1*9* *c*h*a*r*a*c*t*e*r* *s*t*r*i*n*g*
空の文字列の場合、次のように 1 回一致します。
echo String('')->replace('//','*');//Outputs *
以上で私の説明を終わります。正規表現を修正するには、以前に提案されたようにして次を使用します。
/^[^\pL]+|[^\pL]+$/
これにより、正規表現を満たすのに必要な最小量の文字が作成され、望ましくない動作が回避されます。
最後に、正規表現で何をするのか疑問に思っている人の\pL
ために、それは基本的に次のことを意味します: (数字や記号とは対照的に) 任意の文字のような文字に一致します。ここで説明されています:
http://www.php.net/manual/en/regexp.reference.unicode.php