我有(我相信的)否定前瞻断言 <@> *(?!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
。
答案 0 :(得分:10)
匹配,因为零包含在“任何数字”中。因此,没有空格,后跟空格,匹配“任意数量的空格后面没有Q”。
你应该添加另一个先行断言,即你的空格之后的第一件事本身就不是空间。试试这个(未经测试):
<@> *(?!QQQ)(?! )
ETA 旁注:将量词更改为+只有在只有一个空格时才会有所帮助;在一般情况下,正则表达式总是可以占用更少的空间,因此成功。正则表达式想要匹配,并且会以任何可能的方式向后弯曲。所有其他考虑因素(最左边,最长等)都会退居二线 - 如果它可以匹配多种方式,它们会决定选择哪种方式。但匹配总是胜过不匹配。
答案 1 :(得分: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,;
}
你总是要小心*
量词。由于它匹配零次或多次,它也可以匹配空字符串,这基本上意味着:它可以匹配任何字符串中的任何位置。
负面环视断言具有相似的质量,因为它只需要找到一个不同的东西才能匹配。在这种情况下,它会将"<@> "
部分与<@>
+空格+空格匹配,其中空格当然是“不是”QQQ
。你或多或少处于逻辑僵局,因为*
量词和负前瞻相互反击。
我认为解决这个问题的正确方法是将正则表达式分开,就像我上面所说的那样。允许两个正则表达式都被执行的可能性是没有意义的。
然而,出于理论目的,需要锚定一个允许任意数量的空格,和负面前瞻的工作正则表达式。很像Mark Reed所示。这可能是最简单的。
<@>(?! *QQQ) # Add the spaces to the look-ahead
不同之处在于,现在空格和Qs相互锚定,而之前它们可以分别匹配。要将*
量词的重点放在家中,并解决删除其他空格的小问题,您可以使用:
<@> *(?! *QQQ)
这将起作用,因为任何一个量词都可以匹配空字符串。从理论上讲,您可以根据需要添加任意数量的这些内容,但它们没有区别(性能除外):/ * * * * * * */
在功能上等同于/ */
。这里的区别在于可能不存在与Qs结合的空格。
答案 2 :(得分:4)
正则表达式引擎将回溯直到找到匹配,或直到找不到匹配为止。在这种情况下,它找到以下匹配:
+--------------- Matches "<@>".
| +----------- Matches "" (empty string).
| | +--- Doesn't match " QQQ".
| | |
--- ---- ---
'something <@> QQQ' =~ /<@> [ ]* (?!QQQ)/x
你需要做的就是洗牌。取代
/<@>[ ]*(?!QQQ)/
与
/<@>(?![ ]*QQQ)/
或者你可以使正则表达式只匹配所有空格:
/<@>[ ]*+(?!QQQ)/
/<@>[ ]*(?![ ]|QQQ)/
/<@>[ ]*(?![ ])(?!QQQ)/
PS - 很难看到空格,因此我使用[ ]
使它们更加明显。无论如何它都会被优化掉。