当预期不到时,正则表达式负向后观匹配

时间:2017-03-07 15:18:37

标签: regex perl

有人可以帮助我理解为什么以下正则表达式匹配时,我希望它不匹配。

要检查的字符串

/opt/lnpsite/ni00/flat/tmp/Med_Local_Bak/ROI_Med_Transfer/CBD99_PINPUK_14934_09_02_2017_12_07_36.txt

正则表达式

(?<!Transfer\/)\w*PINPUK.*(?:csv|txt)$

我希望这不匹配,因为字符串Transfer/出现在0个或更多字符之后,后面跟着字符串PINPUK。如果我将模式从\w*更改为\w{6}以显式匹配6个字符字符,则会正确返回不匹配项。

有人可以帮助我理解为什么在我的&#34; word&#34;上使用0或更多量词。正则表达式导致匹配?

2 个答案:

答案 0 :(得分:3)

您的正则表达式模式(?<!Transfer/)\w*PINPUK.*(?:csv|txt)$正在寻找\w*PINPUK 而不是,紧接着Transfer/

给出字符串

/opt/lnpsite/ni00/flat/tmp/Med_Local_Bak/ROI_Med_Transfer/CBD99_PINPUK_14934_09_02_2017_12_07_36.txt

正则表达式引擎将首先将\w*PINPUKCBD99_PINPUK

匹配

但是前面有Transfer/,因此引擎回溯并找到BD99_PINPUK

前面有C,不是Transfer/,所以匹配成功

至于修正,只需将斜线放在后面的

之外
(?<!Transfer)/\w*PINPUK.*(?:csv|txt)$

强制\w*在斜杠后立即开始,模式现在正确失败

答案 1 :(得分:2)

鲍罗丁已经很好地解释了为什么这不起作用并且解决了这种情况(移动a /)。有时像这样简单的东西是不可能的,尽管如此我在这里解释一个可能有用的替代工作

如果你将\w*移到负面的后卫中,事情就会像你期望的那样匹配。像这样:

(?<!Transfer\/\w*)PINPUK.*(?:csv|txt)$

不幸的是,Perl不允许这样做,负面观察必须是固定的宽度。但仍有一种方法可以执行一次匹配:反向匹配

^(?:vsc|txt).*KUPNIP(?!\w*\/refsnarT)

这使用了可变长度的负向前瞻,这是Perl允许的。将所有这些放在一个脚本中我们得到

use strict;
use warnings;
use feature 'say';

my $string_matches = '/opt/lnpsite/ni00/flat/tmp/Med_Local_Bak/ROI_Med_Transfer/CBD99_PINPUK_14934_09_02_2017_12_07_36.txt';
say "Trying $string_matches";
if ( reverse($string_matches) =~ /^(?:vsc|txt).*KUPNIP(?!\w*\/refsnarT)/ ) {
    say 'It matched';
} else {
    say 'No match';
}

say '';

my $string_doesnt_match = '/opt/lnpsite/ni00/flat/tmp/Med_Local_Bak/ROI_Med/CBD99_PINPUK_14934_09_02_2017_12_07_36.txt';
say "Trying $string_doesnt_match";
if ( reverse($string_doesnt_match) =~ /^(?:vsc|txt).*KUPNIP(?!\w*\/refsnarT)/ ) {
    say 'It matched';
} else {
    say 'No match';
}

哪个输出

Trying /opt/lnpsite/ni00/flat/tmp/Med_Local_Bak/ROI_Med_Transfer/CBD99_PINPUK_14934_09_02_2017_12_07_36.txt
No match

Trying /opt/lnpsite/ni00/flat/tmp/Med_Local_Bak/ROI_Med/CBD99_PINPUK_14934_09_02_2017_12_07_36.txt
It matched