解析分隔块中的数据

时间:2018-08-21 11:01:40

标签: string perl parsing

我有一个日志文件,其中包含许多块/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': $!";

但是执行后我什么也没得到。有人可以给我建议示例的想法吗?

3 个答案:

答案 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用于警告:

enter image description here

在这段代码中,我没有将值写入新文件,即,不包括>newfile.txt prt,以便您可以看到结果。如果您确实包含该零件,只需打开newfile.txt,所有内容都将在那里打印

答案 2 :(得分:-1)

这是您的代码中的一些问题

  • 您拥有while ($in)而不是while ( <$in> ),因此您的程序从不读取输入文件

  • closewhile读取循环的内部{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

我认为Perl language reference

中没有其他任何过于复杂的内容
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;