如何匹配Perl中模式匹配之前和之后的行?

时间:2010-09-04 08:31:23

标签: regex perl

我正在匹配一个模式并使用$.

获取匹配的行

我需要在特定模式之前和特定模式之后打印匹配的行,例如:

line1
line2
line3
line4
line5

我的模式与line3匹配后,我想打印line2line4

如何在Perl中进行模式匹配?任何人都可以帮助我吗?

提前致谢

塞特希

4 个答案:

答案 0 :(得分:3)

您想要通常称为上下文的内容。获取上下文的最简单方法是使用变量自己维护它:

#!/usr/bin/perl

use strict;
use warnings;

my $old;
while (my $line = <DATA>) {
    if ($line =~ /line3/) {
        print "$old$line", scalar <DATA>;
        last;
    }
    $old = $line;
}

__DATA__
line1
line2
line3
line4
line5

如果您需要多行上下文,最好使用数组:

#!/usr/bin/perl

use strict;
use warnings;

my $context = shift || 3;
if ($context < 0) {
    $context = 0;
}

my @old;
while (my $line = <DATA>) {
    if ($line =~ /line6/) {
        print @old, $line;
        for (1 .. $context) {
            print scalar <DATA>;
        }
        last;
    }
    push @old, $line;
    #remove a line if we have more than we need
    if (@old > $context) {
        shift @old;
    }
}

__DATA__
line1
line2
line3
line4
line5
line6
line7
line8
line9

答案 1 :(得分:3)

将整个文件放在标量中,编写模式,以便捕获line3之前和之后的行。 /m modifier特别有用:

  

将字符串视为多行。也就是说,更改^$以匹配字符串的开头或结尾,以匹配字符串中任何位置的任何行的开头或结尾。

下面的模式使用/x修饰符,它允许我们添加空格,使它们看起来像是匹配的。

例如:

#! /usr/bin/perl

my $data = do { local $/; <DATA> };

my $pattern = qr/ ^(.+\n)
                  ^line3\n
                  ^(.+\n)
                /mx;

if ($data =~ /$pattern/) {
  print $1, $2;
}
else {
  print "no match\n";
}

__DATA__
line1
line2
line3
line4
line5

输出:

line2
line4

请记住$是一个断言:它不会消耗任何字符,因此您必须将换行符与文字\n模式匹配。

另请注意,上述模式缺乏一般性。它适用于中间某处的某条线,但如果您将line3更改为line1line5,则会失败。

对于line1案例,您可以使用?量词使前一行可选:

my $pattern = qr/ ^(.+\n)?
                  ^line1\n
                  ^(.+\n)
                /mx;

正如预期的那样,这会产生

的输出
line2

但是对line5案例

尝试相同的修复
my $pattern = qr/ ^(.+\n)?
                  ^line5\n
                  ^(.+\n)?
                /mx;

给出

no match

这是因为在文件中的最后一个换行符(line5后面的那个换行符)之后,^无处可匹配,但将模式更改为

my $pattern = qr/ ^(.+\n)?
                  ^line5\n
                  (^.+\n)?
                /mx;

输出

line4

我们可能会停在这里,但模式中的不对称是令人不快的。为什么一个案件而不是另一个案件呢?使用line1^匹配$data的开头,然后匹配(.+\n)?的任何内容。

请记住:使用?* 量化的模式始终成功,因为它们在语义上与

相同
  • 零次或一次
  • 零次或多次

分别和任何可以匹配零次:

$ perl -le 'print scalar "abc" =~ /(?!)*/'
1

虽然我想不出我用过这种方式的时间,但{em> m 为零的{m,n}量词,例如

  • {0100}
  • {0,}
  • {0}

将始终成功,因为 m 是最小重复次数。 {0}量词是一个包含完整性的病理案例。

所有这些都表明我们或多或少地对line1案件感到幸运。 ^匹配开头,? - 量化模式未匹配任何内容,然后下一个^也匹配$data的开头。

恢复对称性使得模式更清晰:

my $pattern = qr/ (^.+\n)?
                  ^line5\n
                  (^.+\n)?
                /mx;

答案 2 :(得分:1)

我意识到你要求一个Perl解决方案,但无论如何这里是一个Unix grep解决方案:

grep -C 1 line3 file.txt

输出:

line2
line3
line4

来自grep联机帮助页:

   -C NUM, --context=NUM
    Print  NUM lines of output context.  Places a line containing --
    between contiguous groups of matches.

答案 3 :(得分:1)

使用unix命令行的功能很大就是这种情况,perl接受它。 尝试类似grep -A 1grep -B 1的内容 它会在你之前/之前给你一行