如何从Perl中的文件中读取自定义模式?

时间:2008-12-31 11:16:24

标签: regex perl logging

为所有人带来新年祝福。

我有一个错误日志文件,其中包含模式参数,结果和stderr中的内容(stderr可以是多行)。

$cat error_log

<parameter>:test_tot_count
<result>:1
<stderr>:Expected "test_tot_count=2" and the actual value is 3
test_tot_count = 3
<parameter>:test_one_count
<result>:0
<stderr>:Expected "test_one_count=2" and the actual value is 0
test_one_count = 0
<parameter>:test_two_count
<result>:4
<stderr>:Expected "test_two_count=2" and the actual value is 4
test_two_count = 4
...

我需要在Perl中编写一个函数来将每个参数,result和stderr存储在数组或哈希表中。

这是我们自己内部定义的结构。我写了这样的Perl函数。使用正则表达式本身有更好的方法吗?

my $err_msg = "";
while (<ERR_LOG>)
{
    if (/<parameter>:/)
    {
        s/<parameter>://;
        push @parameter, $_;
    }
    elsif (/<result>:/)
    {
        s/<result>://;
        push @result, $_;
    }
    elsif (/<stderr>:/)
    {
        if (length($err_msg) > 0)
        {
            push @stderr, $err_msg;
        }
        s/<stderr>://;
        $err_msg = $_;
    }
    else
    {
        $err_msg .= $_;
    }
 } 
 if (length($err_msg) > 0)
 {
    push @stderr, $err_msg;
 }

3 个答案:

答案 0 :(得分:4)

如果您使用的是Perl 5.10,那么您可以使用给定/何时结构,执行与现在非常相似但具有更好布局的内容:

use 5.010;

while (<ERR_LOG>) {
    chomp;
    given ($_) {
        when ( m{^<parameter>: (.*)}x )  { push @parameter, $1     }
        when ( m{^<result>:    (.*)}x )  { push @result,    $1     }
        when ( m{^<stderr>:    (.*)}x )  { push @stderr,    $1     }
        default                          { $stderr[-1]   .= "\n$_" }
    }
}

值得注意的是,对于此处的默认情况,我只是在看到@stderr标记并追加到最后一项时,只是推送stderr而不是保留单独的$ err_msg变量。如果我看到一个延续线,则为@stderr数组。我在看到延续线时添加换行符,因为我假设你想要保留它们。

尽管上面的代码看起来非常优雅,但我并非真的所有喜欢保留三个独立阵列的人,因为如果事情不同步,它可能会让你头疼,因为如果你想要在未来添加更多字段,最终会有很多变量浮动,你需要跟踪它们。我建议将每条记录存储在一个哈希中,然后保留一组记录:

use 5.010;

my @records;

my $prev_key;

while (<ERR_LOG>) {
    chomp;
    given ($_) {
        when ( m{^<parameter>  }x ) { push(@records, {}); continue;          }
        when ( m{^<(\w+)>: (.*)}x ) { $records[-1]{$1} = $2; $prev_key = $1; }
        default                     { $records[-1]{$prev_key} .= "\n$_";     }
    }
}

这里我们在看到一个字段时将新记录推送到数组上,每当我们看到一个键/值对时向我们的哈希添加一个条目,如果我们看到一个延续线,则追加到我们添加的最后一个字段。 @records的最终结果如下:

(
    {
        parameter => 'test_one_count',
        result    => 0,
        stderr    => qq{Expected "test_one_count=2" and the actual value is 0\ntest_one_count=0},
    },
    {
        parameter => 'test_two_count',
        result    => 4,
        stderr    => qq{Expected "test_two_count=2" and the actual value is 4\ntest_two_count=4},
    }
)

现在,您只能传递包含所有记录的单个数据结构,并且您可以在将来添加更多字段(即使是多行字段),也可以正确处理它们。

如果你没有使用Perl 5.10,那么这可能是升级的一个很好的借口。如果没有,您可以将给定/何时结构转换为更传统的if / elsif / else结构,但它们在转换中失去了很多美感。

答案 1 :(得分:3)

重构的主要因素是重构,剥离和存储。像这样(未经测试的)代码更简洁:

my( $err_msg , %data );

while (<ERR_LOG>) {
  if(( my $key ) = $_ =~ s/^<(parameter|result|stderr)>:// ) {
    if( $key eq 'stderr' ) {
      push @{ $data{$key} } , $err_msg if $err_msg;
      $err_msg = $_;
    }
    else { push @{ $data{$key} } , $_ }
  }
  else { $err_msg .= $_ }
}

# grab the last err_msg out of the hopper
push @{ $data{stderr} } , $err_msg;

......但是从现在起六个月后可能更难理解...... 8 ^)

答案 2 :(得分:1)

看起来不错。 =)改进可能是将这些标记锚在行的开头:

if (/^<parameter>:/)

它会使脚本更加健壮。

如果您抓住标签之后的内容并仅使用该部分,您也可以避免剥离标签:

if (/^<parameter>:(.*)/s)
{
  push @parameter, $1;
}