【问题标题】:Negative lookahead assertion with the * modifier in PerlPerl 中带有 * 修饰符的负前瞻断言
【发布时间】:2012-08-29 19:45:56
【问题描述】:

我有(我认为是)负前瞻断言<@> *(?!QQQ),如果测试字符串是<@>,后跟任意数量的空格(包括零),我希望匹配它然后 not 后跟 QQQ

然而,如果测试字符串是<@> QQQ,则正则表达式匹配。

我不明白为什么会出现这种情况,希望能在此问题上提供任何帮助。

这是一个测试脚本

use warnings;
use strict;

my @strings = ('something <@> QQQ',
               'something <@> RRR',
               'something <@>QQQ' ,
               'something <@>RRR' );


print "$_\n" for map {$_ . " --> " . rep($_) } (@strings);



sub rep {

  my $string = shift;

  $string  =~ s,<@> *(?!QQQ),at w/o ,;
  $string  =~ s,<@> *QQQ,at w/  QQQ,;

  return $string;
}

打印出来

something <@> QQQ --> something at w/o  QQQ
something <@> RRR --> something at w/o RRR
something <@>QQQ --> something at w/  QQQ
something <@>RRR --> something at w/o RRR

我本来希望第一行是something &lt;@&gt; QQQ --&gt; something at w/ QQQ

【问题讨论】:

    标签: regex perl negative-lookahead regex-lookarounds


    【解决方案1】:

    它匹配是因为“任意数字”中包含零。所以没有空格,后跟一个空格,匹配“任意数量的不跟 Q 的空格”。

    您应该添加另一个前瞻断言,即空格之后的第一件事本身不是空格。试试这个(未经测试):

     <@> *(?!QQQ)(?! )
    

    ETA 旁注:将量词更改为 + 仅在恰好有一个空格时才有帮助;在一般情况下,正则表达式总是可以少占用一个空间并因此成功。正则表达式想要匹配,并且会以任何可能的方式向后弯曲。所有其他考虑因素(最左边、最长等)都处于次要地位 - 如果它可以匹配多个方式,它们将确定选择哪种方式。但匹配总是胜过不匹配。

    【讨论】:

    • (?=\S) 应该是 (?=[^ ])(如果下一个字符是制表符)。实际上,它应该是(?! )(如果它是字符串的结尾)。
    【解决方案2】:
    $string  =~ s,<@> *(?!QQQ),at w/o ,;
    $string  =~ s,<@> *QQQ,at w/  QQQ,;
    

    您的一个问题是您正在分别查看两个正则表达式。您首先要求替换没有QQQ 的字符串,然后用QQQ 替换字符串。从某种意义上说,这实际上是两次检查同一件事。例如:if (X==0) { ... } elsif (X!=0) { ... }。也就是说,代码可能写得更好:

    unless ($string =~ s,<@> *QQQ,at w/  QQQ,) {
        $string =~ s,<@> *,at w/o,;
    }
    

    您必须始终小心* 量词。由于它匹配零次或多次,它也可以匹配空字符串,这基本上意味着:它可以匹配任何字符串中的任何位置。

    否定的环视断言具有相似的质量,因为它只需要找到一个不同的东西才能匹配。在这种情况下,它将"&lt;@&gt; " 部分匹配为&lt;@&gt; + 无空格+ 空格,其中空格当然是“非”QQQ。您在这里或多或少处于逻辑僵局,因为* 量词和负前瞻相互抵消。

    我相信解决这个问题的正确方法是分离正则表达式,就像我上面展示的那样。允许执行两个正则表达式的可能性是没有意义的。

    但是,出于理论上的目的,需要锚定一个允许任意数量的空格、负前瞻的工作正则表达式。就像Mark Reed 所展示的一样。这个可能是最简单的。

    <@>(?! *QQQ)        # Add the spaces to the look-ahead
    

    不同之处在于,现在空格和 Q 彼此锚定,而之前它们可以单独匹配。要了解* 量词的要点,并解决删除额外空格的小问题,您可以使用:

    <@> *(?! *QQQ)
    

    这将起作用,因为任何一个量词都可以匹配空字符串。从理论上讲,您可以根据需要添加任意数量的这些,并且不会产生任何影响(性能除外):/ * * * * * * */ 在功能上等同于/ */。这里的区别在于空格和Qs组合可能不存在。

    【讨论】:

      【解决方案3】:

      正则表达式引擎将回溯,直到找到匹配项,或者直到无法找到匹配项。在这种情况下,它找到了以下匹配项:

                               +--------------- Matches "<@>".
                               |   +----------- Matches "" (empty string).
                               |   |       +--- Doesn't match " QQQ".
                               |   |       |
                              --- ----    ---
      'something <@> QQQ' =~ /<@> [ ]* (?!QQQ)/x
      

      您需要做的就是随机播放。替换

      /<@>[ ]*(?!QQQ)/
      

      /<@>(?![ ]*QQQ)/
      

      或者你可以让正则表达式只匹配所有空格:

      /<@>[ ]*+(?!QQQ)/
      /<@>[ ]*(?![ ]|QQQ)/
      /<@>[ ]*(?![ ])(?!QQQ)/
      

      PS — 空格很难看到,所以我使用[ ] 让它们更明显。无论如何,它都会被优化掉。

      【讨论】:

      • 添加+ 修复了匹配,但我不知道为什么。
      • 等等,我想我明白了。 [ ]*+ 确保即使它破坏了匹配,也会抓取所有可用空间,而 [ ]* 将在不破坏匹配的情况下抓取尽可能多的空间。
      • @flies,因为" " =~ / *+/ 只能匹配" "。它不会回溯到匹配"",因此它不再能找到匹配/ */ 的匹配项。
      • / *+/ 应该意味着“找到零个或多个空格,一次或多次”,究竟是如何工作的? + 贪婪并占用了多余的空间?
      • @TLP,不,当+ 应用于量词(例如*)时,它可以防止通过该量词回溯。 (有点像? 如何修改* 的贪婪。)/ *+//(?&gt; *)/ 是一回事。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-04
      • 1970-01-01
      • 1970-01-01
      • 2015-10-28
      • 1970-01-01
      相关资源
      最近更新 更多