preg_replace双重替换

3
我从正则表达式中得到了一些意外的结果,这个表达式本意是替换命名空间上的类名。替换似乎发生了两次,因此被替换的类名出现了重复(见下面的示例)。
实际上,我通过将正则表达式更改为匹配 1 次或多次(+),而不是 0 次或多次(*),已经解决了这个问题。对于我想要的内容来说,这种方法更准确。
然而,我有一点困惑,不太清楚为什么一开始会出现这个问题。
以下是问题的一个示例:
$classns  = 'components\groups\GroupsController';
$newclass = 'GroupsAccess';
$classns = preg_replace('/[^\\\\]*$/', $newclass, $classns);
echo $classns;

结果

components\groups\GroupsAccessGroupsAccess

预期

components\groups\GroupsAccess

是否有可能星号(*)匹配的是单词边界或其他类似的东西?

对我来说困惑的地方在于,使用相同正则表达式进行preg_match时只显示一个结果,因此看起来是preg_match如何运行正则表达式的特定问题。

例如:

preg_match('/[^\\\\]*$/', $classns, $m);
var_dump($m);

结果

array(1) { [0]=> string(12) "GroupsAccess" }

2
preg_match_all 显示什么?preg_match 总是只显示最多一个匹配。 - John Dvorak
@JanDvorak 很好的发现。它显示了两个匹配项。 - John Kugelman
为什么你不在这里使用str_replace呢? - Evert
@JohnKugelman 这个技巧是 [...]*$ 可以匹配行尾。虽然很难说为什么它在到达结尾后仍然会继续匹配。 - John Dvorak
@Jan Dvorak - 谢谢,奇怪的是,我尝试使用了 trim 函数,但没有效果。它是否匹配实际的结束边界? - Paul S
1
@PaulS 是的。它也可以匹配空字符串。 - John Dvorak
2个回答

5
*不匹配单词边界,而是匹配空字符串。你的表达式首先匹配components\groups\ GroupsController$是一个锚点,它匹配在字符串结束之前的位置(或者在字符串结束之前的\n)。所以,在第一次匹配后,正则解析器的位置在最后一个“r”之后和字符串结尾之前,当它再次尝试匹配你的正则表达式时,它会找到另一个匹配==>0个/(空字符串)后跟字符串的结尾。然后它继续移动,识别字符串的结尾并完成。

我有点错过了“然后它继续前进”的部分(如果匹配为空,则指针会再向前移动一个字符)。现在它完全有意义了。谢谢。 - John Dvorak
在找到第一个匹配项后,后续的搜索将从上一个匹配项的结尾继续进行。空匹配是一种例外情况还是我误解了? - John Dvorak
@JanDvorak,什么是“空匹配”?它之所以匹配,是因为找到了定义的模式(当然是一个空字符串)。正则表达式引擎将始终在上次找到的匹配项之后继续搜索。 - stema
一个空匹配 = 匹配从开始到结束(只有环视和边界)。因此,根据前面的句子,下一个匹配应该从与此相同的位置开始(并导致匹配无限重复)。 - John Dvorak
@JanDvorak,不,如果是这种情况,正则表达式引擎会移动当前位置,否则它会在无限循环中匹配。 - stema
那么,preg_match_all 的文档似乎是不正确/不完整的,因为它没有提到这个(可以预料的)行为,相反,它表明应该发生无限循环。虽然在PHP中存在糟糕的文档也并不奇怪。 - John Dvorak

2
缩小范围后,这也显示了两个匹配项:
preg_match_all('/a*$/', 'a', $m);`

Python 有相同的行为:

>>> re.findall('a*$', 'a')
['a', '']

所以Perl也是如此:
>>> my @m = 'a' =~ /a*$/g;
>>> foreach (@m) { print "$_\n"; }
a
<blank>

似乎正则表达式引擎既匹配字符'a',也匹配其后的空字符串''。尽管这令人惊讶,但从技术上讲是正确的。'a'是一个以搜索字符串结尾为锚点的字符串,因此''也是如此。
匹配的一个基本规则是不重叠。一旦找到匹配项,正则表达式引擎就会在前一个匹配项的结尾继续查找下一个匹配项。但我没想到的是,锚点$可以重复使用,推测这是因为它是零宽断言而不是实际的子字符串匹配。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接