perl regex stop negative look-behind 从下一个贪婪捕获中拿走

perl regex stop negative look-behind taking away from next greedy capture

以 perl v5.22.0 中的这个简单示例为例:

my $data = "foobar\n";
$data =~ s/(?<!bar)(\s*)$/qux/;
print $data;

它打印:

foobar
qux

但我没想到$data会改变。我还尝试了一些早期版本的 perl 5.x,结果相同。

相反,我希望这个具有相同正则表达式的字符串会导致替换,但它不会:

my $data = "foobaz\n";
$data =~ s/(?<!bar)(\s*)$/qux/;
print $data;


我不明白为什么会这样。在任何一个中,星号都应该是贪婪的。我认为 </code> 将是 <code>\n,使负 look-behind 组与第一个示例中的 bar 和第二个示例中的 baz 进行比较。当我使用 perl 时,Regex101 说:

Quantifier: * Between zero and unlimited times, as many times as possible, giving back as needed.

所以在这种情况下会发生什么,它会返回负数 look-behind?

正如标题所说,真正的问题是我想阻止 look-behind 吞下第二组。不幸的是,它不是一个字母,这只是为了让例子更容易理解。同样在 perl 中,我对负数 look-behind 的处理能力有些受限,例如 "Variable length lookbehind not implemented in regex"。如果可能的话,我想要一个与 perl 5.8 兼容的答案。谢谢

它确实匹配了最后一个位置,之前的位置是 \n,之后是 $,现在看看你的正则表达式:

(?<!bar)(\s*)$

前位置不bar:匹配

位置$后匹配(\s*)$

我想你想要

$data =~ s/(?<!bar)(?<!\s)(\s*)$/qux/;

以下版本将适用于 5.8,我认为它实际上更快(因为它跳转到字符串的末尾并回溯而不是在每个位置检查两次向后看):

$data =~ s/
   ^
   (
      (?:
         .*
         (?: [^r\s]
         |   [^a] r
         |   [^b] ar
         )
      )?
   )
   ( \s* )
   \z
/qux/sx;

(可以使用$代替\z;这只是一个微优化。)


说明

没有 m 标志,$ 等同于 (?:\n?\z),也就是说它匹配字符串末尾和字符串末尾的换行符。这意味着 $ 有两个可能的地方匹配 foobar␊

foobar␊      (There's a LF at position 6 in
01234567      case your font can't show it.)
      ^^

(?<!bar) 阻止考虑第一个位置,但允许考虑第二个位置。

  • (?<!bar)(\s*)$ 在位置 7 匹配 0 个字符,因为

    • (?<=bar) 在位置 7 匹配 0 个字符。
    • (\s*) 在位置 7 匹配 0 个字符。
    • $ 在位置 7 匹配 0 个字符。

这是唯一可能的匹配项,因此贪心无关紧要。