15

PHP と Java では/^[^\pL]*|[^\pL]*$/‍‍‍‍<code>-A- に応募し、 を取得し*A**ました。対称パターンを適用し、非対称の結果が得られました! なんで?なぜその出力がそうではないのだろう*A*か?

パターンは、文字列の最後の文字を除くすべてのものを に置き換える必要があることを示しています*。これも貪欲で、文字以外のものをすべて一緒に置き換える必要があります。

また、RegexBuddy に注意してください*A*

更新:主な関心事に焦点を当てるために質問を単純化しました。

4

4 に答える 4

27
#^[^\pL]+|[^\pL]+$#u

に置き換え*ます+*と組み合わせて使用​​しても、期待どおりに$は機能しません。正規表現エンジンの動作の奇妙な結果として、は に2 つの一致をX*$見つけます。を使用すると修正されます。X*+

説明

[^\pL]*$

正規表現のこの部分、期待どおりに機能していない部分を見てみましょう。*一部の文字列の末尾に2 つの 's を付けるのはなぜですか?

  1. ---A---最初の一連のダッシュが置き換えられた後の 3 番目の例の文字列を考えてみましょう。

    *A---$
    
  2. 正規表現エンジンは、次の正規表現に一致するものを見つけます。

    *A---$
      ^
    
  3. "---"アスタリスクに置き換えます。

    *A*$
      ^
    
  4. 次に、内部カーソルを置換文字列の右側に移動します。

    *A*$
       ^
    
  5. このカーソル位置から開始し、別の一致を探します。そして、それは1つを見つけます!""空の文字列が見つかりました。""0 個以上の非文字 ( [^\pL]*) で構成され、文字列 ( ) の末尾に固定されている$ため、有効な一致です。確かに空の文字列が見つかりましたが、それは許可されています。

    $またまたアンカーにマッチしたので意外です。それは間違っていませんか?$それは再び一致するべきではありませんね?実際、そうすべきであり、そうしているのです。は入力文字列の実際の文字ではない$ため、再び一致する可能性があります。これはゼロ幅のアサーションです。$最初の交換で「使い切る」ことはありません。$2 回一致させることができます。

  6. したがって、空の文字列""をアスタリスクに「置き換え」ます。これが、2 つのアスタリスクで終わる理由です。

    *A**$
       ^
    
  7. 正規表現エンジンがステップ 4 に戻った場合、さらに別の空の文字列が検出され、さらに別のアスタリスクが追加されます。概念的に言えば、そこには無数の空の文字列があります。これを回避するために、エンジンは次のマッチが前のマッチと同じ位置で開始することを許可しません。このルールにより、無限ループに陥ることがなくなります。

于 2013-03-29T16:21:52.227 に答える
7

正しい正規表現は次のようになります。

$arr = preg_replace('#^[^\pL]+|[^\pL]+$#','*', 
           array('A','-A-','---A---','-+*A*+-','------------A------------'));

+の代わりに注意してください*。これにより、次の出力が得られます。

Array
(
    [0] => A
    [1] => *A*
    [2] => *A*
    [3] => *A*
    [4] => *A*
)

PS: A の前後に非アルファ文字がないため、最初の要素は変更されないことに注意してください。

于 2013-03-29T15:53:53.760 に答える
2

これを試してみてください:
説明は、コードの後とコードの本体内の両方で、コメントとして提供されます。

<?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

于 2013-04-09T19:30:58.210 に答える