【问题标题】:Negate regular expression in PerlPerl中的否定正则表达式
【发布时间】:2013-07-23 23:25:24
【问题描述】:

我将一个文本文件拆分成块,以便通过使用正则表达式提取那些包含特定行的块。 文本文件如下所示:

[Term]  
id: id1  
name: name1  
xref: type1:aab  
xref: type2:cdc  

[Term]  
id: id2  
name: name2  
xref: type1:aba  
xref: type3:fee 

几天前有人帮助我,向我展示了如何提取那些确实包含某个正则表达式(例如“xref: type3”)的块:

while (<MYFILE>) {
  BEGIN { $/ = q|| }
    my @lines = split /\n/;
    for my $line ( @lines ) {
        if ( $line =~ m/xref:\s*type3/ ) {
            printf NEWFILE qq|%s|, $_;
            last;
        }
    }
}

现在我想在一个不包含“xref: type3”的新文件中写入所有块。我试图通过简单地否定正则表达式来做到这一点

if ( $line !~ m/xref:\s*type3/ )

或者通过使用来否定 if 语句

unless ( $line =~ m/xref:\s*type3/ )

不幸的是,它不起作用 - 输出文件与原始文件相同。任何想法我做错了什么?

【问题讨论】:

  • 在使用unless的时候不要使用!~——这是一个双重否定,和原来的一样。
  • 谢谢,但这只是一个错字。

标签: regex perl negate


【解决方案1】:

不要逐行处理记录。使用段落模式:

{   local $/ = q();
    while (<MYFILE>) {
        if (! /xref:\s*type3/ ) {
            printf NEWFILE qq|%s|, $_;
            last;
        }
}

【讨论】:

  • 感谢您的帮助。我尝试了这个解决方案,但它只为我返回了第一个块。我需要添加一个循环来处理所有段落吗?
  • @user2241303:如果你想要所有的块,删除last 命令。
【解决方案2】:

问题是您将unless!~ 一起使用,这被解释为好像$line 匹配这样做。 (双重否定)

当将unless 块与普通模式匹配运算符=~ 一起使用时,您的代码可以完美运行,即我将第一个块视为输出,因为它不包含type3。

LOOP:
while (<$MYFILE>) {
  BEGIN { $/ = q|| }
    my @lines = split /\n/;
    for my $line ( @lines ) {
        unless ( $line =~ m/xref:\s*type3/ ) {
            printf qq|%s|, $_;
            last LOOP;
        }
  }
}

# prints
# [Term]
# id: id1
# name: name1
# xref: type1:aab
# xref: type2:cdc

【讨论】:

  • 感谢您的帮助,但我得到的只是一个空文档,尽管我复制了代码并使用了完全相同的示例。
  • 我将 printf 语句更改为打印到我的控制台,添加您的 NEWFILE 句柄以检查或检查您的终端。
  • 我添加了我的 NEWFILE 句柄,但输出文件仍然是空的。我真的不知道该怎么办。 #!/usr/bin/perl open (MYFILE, 'inputfile'); open (NEWFILE, "&gt;&gt;", 'outputfile'); LOOP: while (&lt;$MYFILE&gt;) { BEGIN { $/ = q|| } my @lines = split /\n/; for my $line ( @lines ) { unless ( $line =~ m/xref:\s*type3/ ) { printf NEWFILE qq|%s|, $_; last LOOP; } } }
  • MYFILE 之前取出$,如果你有strictwarnings 你会看到错误。
【解决方案3】:

你有:

对于每一行,如果此行与模式不匹配,则打印此块。

但你想要:

对于每一行,如果块中没有其他行与模式匹配,则打印这一行。

因此,您不能在检查块中的每一行之前开始打印该块(或在找到匹配行之前的所有行)。

local $/ = q||;
while (<MYFILE>) {
    my @lines = split /\n/;

    my $skip = 0;
    for my $line ( @lines ) {
        if ( $line =~ m/^xref:\s*type3/ ) {
            $skip = 1; 
            last;
        }
    }

    if (!$skip) {
        for my $line ( @lines ) {
            print NEWFILE $line;
        }
    }
}

但是没有必要分成几行。我们可以一次检查并打印整个块。

local $/ = q||;
while (<MYFILE>) {
    print NEWFILE $_ if !/^xref:\s*type3/m;
}

(注意/m 使^ 匹配任何行的开头。)

【讨论】:

    猜你喜欢
    • 2011-09-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-17
    • 1970-01-01
    • 2012-07-22
    • 1970-01-01
    相关资源
    最近更新 更多