2010-09-04 95 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