2010-09-04 119 views
1

我与模式匹配并获得使用$.如何匹配Perl中模式匹配前后的行?

我所需要的特定的图在特定的模式后打印线匹配的匹配线,如:

line1 
line2 
line3 
line4 
line5 

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

如何在Perl中进行模式匹配?谁能帮我?

在此先感谢

塞特希

+2

到目前为止你有什么特点?如果更多的线路匹配呢?甚至有两条相应的线? – pavel 2010-09-04 09:35:37

回答

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

我知道你问一个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. 
+0

但'grep'没有'perl'那样好的正则表达式引擎。为了简化'grep',但是'perl'正则表达式的威力,可以使用['ack'](http://search.cpan.org/dist/ack/ack-base)来代替:'ack - C 1 line3 file.txt' – 2010-09-04 13:28:53

+0

要在Unix grep中使用Perl常规experssion语法,请使用'grep -P' – toolic 2010-09-04 14:26:58

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

虽然我不认为我见过它使用这种方式时,一个{m,n}量词其中为零,例如

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

将总是成功,因为是重复的最小数目。量词是一个包含完整性的病理案例。

所有这些都表明我们或多或少得到line1的幸运。 ^匹配的最开始,?-量化模式没有匹配,然后下一个^也匹配$data的最开始。

恢复对称性使得清洁器模式:

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

使用unix命令行功率大是这样的情况下和perl拥抱它。 尝试类似grep -A 1grep -B 1 它会给你之前/之前的行

+0

噢,虽然上面的解决方案可以工作,但它们是非常难以编码的,并且在这种情况下不需要一个案例 – Noam 2010-09-04 12:20:08