负向先行断言正则表达式

3
为什么不起作用?试图进行负向前瞻。我正在尝试从箱子中取出数字,除了隔离箱和检查箱。当我在代码中使用^时,它会匹配括号中的所有数字。当我删除^时,它就什么也匹配不到。
你能在负向前瞻中使用“或运算符|”吗?我想要^(?!Quarantine_Bin | Inspection_Bin)。
我还试图具体否定 [^Quarantine_Bin],但它仍然匹配。
^(?!Quarantine_Bin)\([0-9]+\)

数据

    Quarantine(2),Other_Bin(2),Quarantine_Bin(2),Quarantine_Bin(2),
    Quarantine_Bin(5),Inspection_Bin(3),Regular_Bin(5),other(2)

那么,只应该从上面的字符串中提取25吗?https://regex101.com/r/UAlyEG/1? - Wiktor Stribiżew
4
老实说,我建议不要像这样使用正则表达式,而是使用 split /,/ 将值拆分并将其作为单独的测试进行匹配。复杂的正则表达式不利于程序维护。 - Sobrique
@Sobrique 每行可以有不同数量的垃圾箱,所以您认为分割是最好的方法吗? - nzaleski
4个回答

5

这是一个负向的回顾后发断言

use warnings;
use feature 'say';

my @strings = (
    "Quarantine_Bin(5),Inspection_Bin(3),Regular_Bin(5),other(2)",
    "Quarantine(2),Other_Bin(2),Quarantine_Bin(2),Quarantine_Bin(2),"
);

for (@strings) {
    my @m = $_ =~ /(?<!\b(?:Quarantine|Inspection)_Bin)\(\d+\)/g; 
    say "@m";
} 
^锚点在这里并不能达到你想要的效果,应该使用\b来指定单词边界。

这将包括具有数字的括号,返回行(5) (2)(2) (2)

如果你想省略它们,可以在数字周围添加捕获括号。

/(?<! \b(?: Quarantine|Inspection)_Bin ) \( (\d+) \)/xg;

或者将开括号放入后顾断言内(这样它就不会被消耗),并省略闭括号。
/(?<! \b(?: Quarantine|Inspection)_Bin \( ) \d+/xg;

这些返回行5 22 2,没有圆括号。 /x修饰符允许在内部添加空格以提高可读性。

4

您应该使用负回顾后断言,如下所示:

(?<!\b(Quarantine|Inspection)_Bin)\([0-9]+\)

RegEx演示

(?<!\b(Quarantine|Inspection)_Bin)是一个负向后查找,如果我们的匹配前面有Quarantine_BinInspection_Bin,则会失败。 \b是用于单词边界的符号。


2

^(?!Quarantine_Bin)\([0-9]+\) 检查字符串开头是否不是后跟 Quarantine_Bin 但后跟 \([0-9]\)。这是永远不可能成立的。

[^Quarantine_Bin] 匹配除了 B, Q, a, e, i, n, r, t, u_ 的单个字符。这不是你想要的。


如果没有过滤,您将会得到

\b\w+\([0-9]+\)

您希望确保\b后面不是跟着Quarantine_Bin或者Inspection_Bin,这时候您可以使用:

\b(?!Quarantine_Bin\b)(?!Inspection_Bin\b)\w+\([0-9]+\)

或者

\b(?!(?:Quarantine|Inspection)_Bin\b)\w+\([0-9]+\)

\b 在预查中防止了 Quarantine_Bin_X 被过滤掉。


有用的:

(?:(?!STRING).)* 的作用类似于 [^CHAR]* 对于 CHAR 的作用。


1

这是在评论中提到的,我会将其详细说明为实际答案。

我建议通常避免使用前瞻/后顾正则表达式,因为它可能会变得复杂和混乱。在您的用例中 - 我可能只会将该行 split 成一个数组,并单独处理每个元素。

类似于:

#!/usr/bin/env perl
use strict;
use warnings;

while ( <DATA> ) { 
    chomp;
    #split on comma;
    #grep out Inspection_Bin and Quarantine_Bin
    my @fields = grep { not m/(?:Quarantine|Inspection)_Bin/ } split /,/;
    #iterate each field, and select out two different regex matches, e.g.
    #word bit and number bit. 
    print m/^(\w+)/, "=>", m/\((\d+)\)/, "\n" for @fields;
}


__DATA__
Quarantine(2),Other_Bin(2),Quarantine_Bin(2),Quarantine_Bin(2),Quarantine_Bin(5),Inspection_Bin(3),Regular_Bin(5),other(2)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接