繁体   English   中英

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

[英]How can I read a custom defined pattern from a file in Perl?

祝大家新年快乐。

我有一个错误日志文件,其内容包含在pattern参数,result和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中编写一个函数,以将每个参数,结果和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;
 }

如果您使用的是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$_" }
    }
}

值得一提的是默认情况下在这里,而不是保持一个独立的$ ERR_MSG变量,我只是推到@stderr当我看到一个stderr标签,并追加到的最后一个项目@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 / elif / else结构,但是它们在转换中失去了很多美感。

保罗

跳转到重构的主要内容是匹配,剥离和存储中的重复。 像这样(未经测试)的代码更为简洁:

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 ^)

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

if (/^<parameter>:/)

这会使脚本更加健壮。

如果捕捉到标记后面的内容并仅使用该部分,也可以避免标记的剥离:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM