使用Perl捕获输出,直到找到特定模式

时间:2012-02-18 03:10:19

标签: regex perl parsing

我觉得我在这里错过了一些非常简单的东西,但这是我第一次需要这样做而且找不到一个例子。

我有一个巨大的foreach循环,它遍历输出日志并根据匹配的正则表达式提取各种信息。我的主要问题是一些较大类型的输出有一个页眉和页脚,如*** Begin bangle tracking log***后跟几行乱码,然后是***End bangle tracking log***

是否有一种方法,从foreach循环内部,有一个内部循环存储所有行,直到找到页脚?

foreach my $line( @parseme )
{
    if( $line =~ m/***Begin bangle tracking log***/ )
    {
        #Help! Push all lines into an array until bangle tracking footer is found.
    }
    if( $line =~ m/Other stuff I am tracking/ )
    {
        #Do other things
    }
}

4 个答案:

答案 0 :(得分:5)

您可以使用range operator,它在标量上下文中充当触发器:

foreach ( @parseme ) {
    if ( /Begin bangle tracking log/ .. /End bangle tracking log/ ) {
        push @array, $_;
    }
    # other stuff...
}

我将$_用于foreach循环,因为它允许更简洁的语法。如果您愿意,可以使用另一个变量,但是您必须将条件写为:

if ( $line =~ /Begin .../ .. $line =~ /End .../ ) {

使用一些额外的括号可能更具可读性:

if ( ($line =~ /Begin .../) .. ($line =~ /End .../) ) {

关于触发器操作符需要注意的一个问题是即使在循环结束后它也会记住它的状态。这意味着,如果您打算再次运行循环,那么确实应确保@parseme数组以与/End .../正则表达式匹配的行结束,以便当下一次循环开始时,触发器将处于已知状态。

编辑:根据下面的DVK评论,如果您想在到达页脚行时立即处理收集的行,可以通过检查..的返回值来执行此操作运算符,将在最后一行以E0结尾:

foreach ( @parseme ) {
    my $in_block = /Begin bangle tracking log/ .. /End bangle tracking log/;
    if ( $in_block ) {
        push @array, $_;
    }
    if ( $in_block =~ /E0$/ ) {  # last line
        # process the lines in @array
        @array = ();
    }
    # other stuff...
}

答案 1 :(得分:4)

通过实现原始状态机,您可以轻松地做到这一点:

my $inside_bangle = 0; # 0=outside block, 1=inside
my @buffer;
foreach my $line( @parseme ) {
    if  ($line =~ m/***Begin bangle tracking log***/ ) {
        $inside_bangle = 1;
        next;
    }
    if ($line =~ m/***End bangle tracking log***/ ) {
        $inside_bangle = 0;
        # PROCESS @buffer somehow
        next;
    }
    if ($inside_bangle) {
        push @buffer, $line;
        next;
    }
    if ($line =~ m/other stuff i am tracking/ ) {
        #Do other things
    }
}

另一种选择是使用触发器(..

答案 2 :(得分:1)

您可能正在寻找..运算符,该运算符在应用正则表达式时具有一些神奇的属性。以下示例是从PLEAC

中窃取的
while (<>) {
    if (/BEGIN PATTERN/ .. /END PATTERN/) {
        # line falls between BEGIN and END in the
        # text, inclusive.
    }
}

在块中,根据需要附加到数组变量。

答案 3 :(得分:0)

现在,您可以存储多个日志片段实例,如果它出现多次(DVK的原始代码):

my $inside_bangle = 0; # 0=outside block, 1=inside
my %buffer;
my $index = 0;
foreach my $line( @parseme ) {
    if  ($line =~ m/***Begin bangle tracking log***/ ) {
        $inside_bangle = 1;
        next;
    }
    if ($line =~ m/***End bangle tracking log***/ ) {
        $inside_bangle = 0;
        $index++;
        # PROCESS @buffer somehow
        next;
    }
    if ($inside_bangle) {
        push @{ $buffer{$index} }, $line;
        next;
    }
    if ($line =~ m/other stuff i am tracking/ ) {
        #Do other things
    }
}

这是我最初写的内容,但我认为DVK的代码更具可读性和整洁性:

open FILE, "<", 'testfile.log';
@parseme = <FILE>;
my $initialize = shift @parseme;
my $startLogging = $initialize =~ m/^Log Start$/ ? 1 : 0; # test if first line of array is start of log
my %storage = ();
my $index = 0;
foreach my $line (@parseme) {
 $startLogging = 1 if $line =~ m/^Log Start$/;
 if ($startLogging == 1) {
  push( @{ $storage{$index} }, $line ) unless $line =~ m/(Log Start|Log End)$/;
  if ($line =~ m/^Log End$/) {
   $startLogging = 0;
   $index++;
  }
 }
}