我有一个日志文件,其中包含许多块/begin CHECK
... /end CHECK
,如下所示:
/begin CHECK
Var_AAA
"Description AAA"
DATATYPE UBYTE
Max_Value 255.
ADDRESS 0xFF0011
/end CHECK
/begin CHECK
Var_BBB
"Description BBB"
DATATYPE UBYTE
Max_Value 255.
ADDRESS 0xFF0022
/end CHECK
...
我要提取变量名及其地址,然后像这样写入新文件
Name Address
Var_AAA => 0xFF0011
Var_BBB => 0xFF0022
我只是想($start, $keyword, $end)
检查每个块并仅在关键字之后提取数据
#!/usr/bin/perl
use strict;
use warnings;
my $input = 'input.log';
my $output = 'output.out';
my ( $start, $keyword, $end ) = ( '^\/begin CHECK\n\n', 'ADDRESS ', '\/end CHECK' );
my @block;
# open input file for reading
open( my $in, '<', $input ) or die "Cannot open file '$input' for reading: $!";
# open destination file for writing
open( my $out, '>', $output ) or die "Cannot open file '$output' for writing: $!";
print( "copying variable name and it's address from $input to $output \n" );
while ( $in ) { #For each line of input
if ( /$start/i .. /$end/i ) { #Block matching
push @block, $_;
}
if ( /$end/i ) {
for ( @block ) {
if ( /\s+ $keyword/ ) {
print $out join( '', @block );
last;
}
}
@block = ();
}
close $in or die "Cannot close file '$input': $!";
}
close $out or die "Cannot close file '$output': $!";
但是执行后我什么也没得到。有人可以给我建议示例的想法吗?
答案 0 :(得分:3)
大多数事情看起来都不错,但是导致第一个问题的是您的正则表达式:
'^\/begin CHECK\n\n'
您正在从文件中读取行,但随后连续查找两个换行符。那将永远不会匹配,因为一行仅以一个换行符结尾(除非您更改$/
,但这是一个不同的主题)。如果要匹配行的发送,可以使用$
(或\z
)锚点:
'^\/begin CHECK$'
这是我削减的程序。您可以对其进行调整,以完成您需要做的所有其他工作:
use v5.10;
use strict;
use warnings;
use Data::Dumper;
my ($start, $keyword, $end) = (qr{^/begin CHECK$}, qr(^ADDRESS ), qr(^/end CHECK));
while (<DATA>) #For each line of input
{
state @block;
chomp;
if (/$start/i .. /$end/i) #Block matching
{
push @block, $_ unless /^\s*$/;
}
if( /$end/i )
{
print Dumper( \@block );
@block = ();
}
}
在那之后,您将不读取数据。您需要将文件句柄放在<>
(行输入运算符)内:
while ( <$in> )
文件句柄将在程序末尾自动关闭。如果您想自己关闭它们,那很好,但是直到完成后再这样做。在$in
完成之前,请勿关闭while
。
答案 1 :(得分:1)
在Windows中使用命令提示符。在MacOS或Unix中,您将遵循相同的逻辑:
perl -wpe "$/='/end CHECK';s/^.*?(Var_\S+).*?(ADDRESS \S+).*$/$1 => $2\n/s" "your_file.txt">"new.txt
首先,我们将endLine字符设置为$/ = "/end CHECK".
然后,我们仅选择第一个Var_
和第一个ADDRESS.
,同时以单行模式删除所有其他内容,即点匹配换行符\ n。 s/^.*?(Var_\S+).*?(ADDRESS \S+).*$/$1 => $2\n/s
。
然后,我们将结果写入新文件。即> newfile。
确保使用-w -p -e,其中-e用于执行代码,-p用于打印,-w用于警告:
在这段代码中,我没有将值写入新文件,即,不包括>newfile.txt
prt,以便您可以看到结果。如果您确实包含该零件,只需打开newfile.txt
,所有内容都将在那里打印
答案 2 :(得分:-1)
这是您的代码中的一些问题
您拥有while ($in)
而不是while ( <$in> )
,因此您的程序从不读取输入文件
您close
在while
读取循环的内部{em> 输入文件句柄,因此您只能读取一条记录
您的$start
正则表达式模式为'^\/begin CHECK\n\n'
。单引号使您的程序搜索backslash n backslash n
而不是newline newline
您的测试if (/\s+ $keyword/)
将查找多个各种空格字符,后跟一个空格,后跟ADDRESS
-$keyword
的内容。在数据中的任何位置,ADDRESS
之前都没有空格
您还编写了太多了,没有进行任何测试。您应该首先编写自己的读取循环,并在每次测试之间一次添加两到三行代码之前,确保数据正确输入。在测试之前编写90%的功能是一种非常糟糕的方法。
将来,为帮助您解决此类问题,我将向您介绍Stack Overflow Perl tag information page
上链接的出色资源这里唯一有点晦涩的是 range运算符 /$start/i .. /$end/i
返回了一个有用的值;我已将其复制到$status
中。运算符第一次匹配时,结果将为1;第二次是2,等等。最后一次是不同的,因为它是一个使用工程表示法的字符串,例如9E0
,因此它仍然计算为正确的计数,但是您可以使用/E/
检查最后一个匹配项。我已使用== 1
和/E/
来避免将开始和结束行推到@block
use strict;
use warnings;
use autodie; # Handle bad IO status automatically
use List::Util 'max';
my ($input, $output) = qw/ input.log output.txt /;
open my $in_fh, '<', $input;
my ( @block, @vars );
while ( <$in_fh> ) {
my $status = m{^/begin CHECK}i .. m{^/end CHECK}i;
if ( $status =~ /E/ ) { # End line
@block = grep /\S/, @block;
chomp @block;
my $var = $block[0];
my $addr;
for ( @block ) {
if ( /^ADDRESS\s+(0x\w+)/ ) {
$addr = $1;
last;
}
}
push @vars, [ $var, $addr ];
@block = ();
}
elsif ( $status ) {
push @block, $_ unless $status == 1;
}
}
# Format and generate the output
open my $out_fh, '>', $output;
my $w = max map { length $_->[0] } @vars;
printf $out_fh "%-*s => %s\n", $w, @$_ for [qw/ Name Address / ], @vars;
close $out_fh;
Name => Address
Var_AAA => 0xFF0011
Var_BBB => 0xFF0022
对于它的价值,我会写这样的东西。它产生与上面相同的输出
use strict;
use warnings;
use autodie; # Handle bad IO status automatically
use List::Util 'max';
my ($input, $output) = qw/ input.log output.txt /;
my $data = do {
open my $in_fh, '<', $input;
local $/;
<$in_fh>;
};
my @vars;
while ( $data =~ m{^/begin CHECK$(.+?)^/end CHECK$}gms ) {
my $block = $1;
next unless $block =~ m{(\w+).+?ADDRESS\s+(0x\w+)}ms;
push @vars, [ $1, $2 ];
}
open my $out_fh, '>', $output;
my $w = max map { length $_->[0] } @vars;
printf $out_fh "%-*s => %s\n", $w, @$_ for [qw/ Name Address / ], @vars;
close $out_fh;