在 PHP 和 Java 中,我申请/^[^\pL]*|[^\pL]*$/
了 <code>-A- 并获得了*A**
. 我应用了对称图案并得到了不对称的结果!为什么?我想知道为什么它的输出不是*A*
?
模式说字符串末尾除了字母之外的所有东西都应该替换为*
,它也是贪婪的,应该将所有非字母的东西一起替换。
RegexBuddy 中的 Alos 注释我明白*A*
这是我所期望的。
更新:我简化了问题以集中我的主要关注点。
#^[^\pL]+|[^\pL]+$#u
替换*
为+
。*
与预期结合使用并不$
完全有效。在正则表达式引擎如何工作的一个奇怪结果中,X*$
将找到两个匹配的X*
. 使用+
修复它。
[^\pL]*$
让我们看一下正则表达式的这一部分,即没有按预期工作的部分。为什么它*
在一些字符串的末尾放了两个?
---A---
考虑替换第一组破折号后的第三个示例字符串:
*A---$
正则表达式引擎在这里找到正则表达式的匹配项:
*A---$
^
并用"---"
星号替换:
*A*$
^
然后它将其内部光标移动到替换字符串的右侧。
*A*$
^
它从这个光标位置开始并寻找另一个匹配。它找到了一个!它找到了""
——空字符串!""
由 0 个或多个非字母 ( [^\pL]*
) 组成,并且它锚定在字符串 ( ) 的末尾$
,因此它是有效匹配。它确实找到了空字符串,但这是允许的。
这是出乎意料的,因为它$
再次匹配了锚。那不是错了吗?$
它不应该再次匹配,不是吗?好吧,实际上,它应该而且确实如此。它可以再次匹配$
,因为$
它不是输入字符串中的实际字符——它是一个零宽度断言。它不会被第一次替换“用完”。$
允许匹配两次。
因此,它""
用星号“替换”空字符串。这就是为什么你最终会得到两个星号。
*A**$
^
如果正则表达式引擎返回到第 4 步,它将找到另一个空字符串并添加另一个星号。从概念上讲,那里有无数个空字符串。为了避免这种情况,引擎不允许下一场比赛在与前一场比赛相同的位置开始。此规则可防止它进入无限循环。
正确的正则表达式是这样的:
$arr = preg_replace('#^[^\pL]+|[^\pL]+$#','*',
array('A','-A-','---A---','-+*A*+-','------------A------------'));
注意+
而不是*
. 这将给出输出:
Array
(
[0] => A
[1] => *A*
[2] => *A*
[3] => *A*
[4] => *A*
)
PS:请注意,由于A 前后没有非字母字符,因此第一个元素将保持不变。
试一试:
在代码之后和代码主体中都给出了解释——作为注释。
<?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
_ _ _
^
由于指针指向空字符串""
,因此它会根据我们的正则表达式对其进行测试。
大多数正则表达式将无法通过此测试,因为满足正则表达式所需的最少字符数通常是一个或多个。但在我们的例子中,匹配是成功的,因为 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*
echo String('')->replace('//','*');//Outputs *
我的解释到此结束。要修复您的正则表达式,请按照之前的建议进行操作并使用:
/^[^\pL]+|[^\pL]+$/
这将使满足正则表达式所需的最少字符数,从而解决不需要的行为。
最后一点,如果有人想知道\pL
正则表达式中的作用,它基本上意味着:匹配任何类似字母的字符(而不是数字或符号)。在这里解释:
http ://www.php.net/manual/en/regexp.reference.unicode.php
/^[^\pL]*|[^\pL]*$/
['A','-A-','---A---','-+*A*+-','------------A------------']
也许我误解了问题或正则表达式,但看起来它与两个选项之一匹配
选项 1:它匹配/^
换行符或字符串的开头。然后它匹配一个不是字母的字符零次或多次
所以理论上-A
, =A
, -
,=-+_+_==-=~````~!@#$A
甚至=-+_+_==-=~~!@#
会匹配这个。
选项 2:它匹配非字母零次或多次,然后匹配字符串或行的结尾