7

如果测试的字符串是 a后跟 任意数量的空格(包括零),然后跟.<@> *(?!QQQ)<@>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 <@> QQQ --> something at w/ QQQ.

4

3 回答 3

10

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

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

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

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

于 2012-04-27T11:53:29.157 回答
7
$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,;
}

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

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

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

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

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

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

<@> *(?! *QQQ)

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

于 2012-04-27T14:09:23.473 回答
4

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

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

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

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

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

或者你可以这样做,这样正则表达式将只匹配所有空格:

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

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

于 2012-04-27T15:17:01.793 回答