[英]In Perl, how can I print lines read from a file with optional leading whitespace removed?
[英]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.