为什么这个正向后行断言在锚定到字符串开头时不起作用?

3
为什么这个后顾断言在被锚定到字符串前面时不起作用?运行以下代码,你会发现第一个测试通过了,但是第二个测试只有一个变化,即^ 锚点,却失败了。
use Test::More tests => 2;

my $s = '/123/456/hello';    
$s =~ s{(?<=/)\d+(?=/\d+/hello)}{0};  # unanchored
is($s, '/0/456/hello', 'unanchored'); # passes

$s = '/123/456/hello';
$s =~ s{^(?<=/)\d+(?=/\d+/hello)}{0}; # anchored
is($s, '/0/456/hello', 'anchored');   # fails

对于我来说,把^放到向后查找断言中不是一个选项(这只是一个极为简化的例子),但这确实解决了问题。我已经找到了另一种实现我想要的功能的方法,但我很好奇为什么这种方法行不通。我已经在Perl 5.8.8和Perl 5.10.0上进行了测试。

4个回答

9

请记住,断言是零宽度的,不会消耗匹配的字符。因此锚点必须放在断言内部,否则整个表达式将无法匹配。


好的观点。它不能既是字符串的开头又有一个'/'在它之前。 - Axeman
回顾断言是在\d+之前查找,而不是在^之前。它不匹配,因为该断言不会消耗其匹配的字符,这样在应用断言后的“有效正则表达式”就变成了^\d+,当然无法匹配/123/456/hello - John Siracusa

6

(?<=/)\d+(?=/hello) 在你的字符串中匹配 456,因为它是该字符串中两个环视都适用的唯一部分。当你锚定你的表达式时,它将不再匹配任何内容。环视是零宽度的,所以你的第二个模式表示“从字符串开头开始匹配一个或多个数字,其中前面的字符是斜杠”,这显然是不可能的。


所有答案大同小异,但这一个似乎最清晰。 - ysth

4
我猜是因为(?<=是正向后瞻(不是负向),而且你不能在字符串的开头之前有一个字符。如果你想要负向后瞻,应该使用(?

正/负的问题是一个错误的问题;现在已经修复。 - John Siracusa

1

字符串前面没有任何内容,因此当锚定使用^时,任何非空的反向引用都将失败。


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