通过总结由空行分隔的每行的不同列来解析文件

时间:2016-05-13 16:14:09

标签: perl

我有一个文件输入如下;

volume stats
start_time  1
length      2
--------
ID
0x00a,1,2,3,4
0x00b,11,12,13,14
0x00c,21,22,23,24

volume stats
start_time  2
length      2
--------
ID
0x00a,31,32,33,34
0x00b,41,42,43,44
0x00c,51,52,53,54

volume stats
start_time  3
length      2
--------
ID
0x00a,61,62,63,64
0x00b,71,72,73,74
0x00c,81,82,83,84

我需要以下格式输出;

1 33    36  39  42
2 123   126 129 132
3 213   216 219 222

以下是我的代码;

#!/usr/bin/perl
use strict;
use warnings;
#use File::Find;

# Define file names and its location
my $input = $ARGV[0];

# Grab the vols stats for different intervals
open (INFILE,"$input") or die "Could not open sample.txt: $!";
my $date_time;
my $length;
my $col_1;
my $col_2;
my $col_3;
my $col_4;
foreach my $line (<INFILE>)
{

    if ($line =~ m/start/)
        {
            my @date_fields = split(/   /,$line);
            $date_time = $date_fields[1];
        }
    if ($line =~ m/length/i)
        {
            my @length_fields = split(/ /,$line);
            $length = $length_fields[1];
        }
    if ($line =~ m/0[xX][0-9a-fA-F]+/)
        {
            my @volume_fields = split(/,/,$line);
            $col_1 += $volume_fields[1];
            $col_2 += $volume_fields[2];
            $col_3 += $volume_fields[3];
            $col_4 += $volume_fields[4];
            #print "$col_1\n";
        }
    if ($line =~ /^$/)
        {
            print "$date_time $col_1 $col_2 $col_3 $col_4\n";
                $col_1=0;$col_2=0;$col_3=0;$col_4=0;
        }
}
close (INFILE);

我的代码结果是;

1
 33 36 39 42
2
 123 126 129 132

基本上,对于每个时间间隔,它只是汇总所有行的列,并显示每个时间间隔的所有列。

3 个答案:

答案 0 :(得分:1)

$/是你的朋友。尝试将其设置为''以启用段落模式(用空行分隔数据)。

#!/usr/bin/env perl

use strict;
use warnings;

local $/ = ''; 

while ( <> ) {
    my ( $start ) = m/start_time\s+(\d+)/;
    my ( $length ) = m/length\s+(\d+)/;
    my @row_sum; 
    for ( m/(0x.*)/g )  {
        my ( $key, @values ) = split /,/; 
        for my $index ( 0..$#values ) {
           $row_sum[$index] += $values[$index];
        }
    }
    print join ( "\t", $start, @row_sum ), "\n";
}

输出:

1       33      36      39      42
2       123     126     129     132
3       213     216     219     222

注意 - 使用制表位输出。如果您需要更灵活的选项,可以使用sprintf

我还建议代替:

my $input = $ARGV[0]; 
open (my $input_fh, '<', $input) or die "Could not open $input: $!";

你会更好:

while ( <> ) { 

因为<>是perl中的魔术文件句柄,所以 - 打开在命令行上指定的文件,并一次读取一个文件,如果没有,则读取STDIN。这就像grep / sed / awk这样做的方式。

因此,您仍然可以使用scriptname.pl sample.txt运行此功能,或者您可以执行curl http://somewebserver/sample.txt | scriptname.plscriptname.pl sample.txt anothersample.txt moresample.txt

另外 - 如果您想自己open文件,最好使用词法变量和3 arg打开:

open ( my $input_fh, '<', $ARGV[0] ) or die $!; 

你真的不应该使用像$col_1这样的'编号'变量等。如果有数字,那么数组几乎总是更好。

答案 1 :(得分:1)

基本上,一个块以start_time开头,以一行空格结束。如果相反,块的结尾始终确保为空行,则可以更改下面的测试。

使用arrays instead of variables with integer suffixes会很有帮助。

当您点击新块的开头时,记录start_time值。当您点击统计行时,更新列总和,当您点击一行空格时,打印列总和并清除它们。

这样,您可以将程序的内存占用与最大输入行成比例,与最大输入块相对应。在这种情况下,没有太大的区别,但是,在现实生活中,可以有。您的原始程序将整个文件作为行列表读入内存,当使用大输入大小时,这些行将导致程序的内存占用量增加。

#!/usr/bin/env perl

use strict;
use warnings;

my $start_time;
my @cols;

while (my $line = <DATA>) {
    if ( $line =~ /^start_time \s+ ([0-9]+)/x) {
        $start_time = $1;
    }
    elsif ( $line =~ /^0x/ ) {
        my ($id, @vals) = split /,/, $line;
        for my $i (0 .. $#vals) {
            $cols[ $i ] += $vals[ $i ];
        }
    }
    elsif ( !($line =~ /\S/) ) {
        # guard against the possibility of
        # multiple blank/whitespace lines between records
        if ( @cols ) {
            print join("\t", $start_time, @cols), "\n";
            @cols = ();
        }
    }
}

# in case there is no blank/whitespace line after last record
if ( @cols ) {
    print join("\t", $start_time, @cols), "\n";
}

__DATA__
volume stats
start_time  1
length      2
--------
ID
0x00a,1,2,3,4
0x00b,11,12,13,14
0x00c,21,22,23,24

volume stats
start_time  2
length      2
--------
ID
0x00a,31,32,33,34
0x00b,41,42,43,44
0x00c,51,52,53,54

volume stats
start_time  3
length      2
--------
ID
0x00a,61,62,63,64
0x00b,71,72,73,74
0x00c,81,82,83,84

输出:

1  33  36  39  42
2   123 126 129 132
3   213 216 219 222

答案 2 :(得分:0)

当我运行你的代码时,我会收到警告:

Use of uninitialized value $date_time in concatenation (.) or string

我使用\s+代替/ /修复了它。

我还在循环后添加了print,以防文件没有以空行结束。

这是最低限度更改的代码,用于生成所需的输出:

use strict;
use warnings;

# Define file names and its location
my $input = $ARGV[0];

# Grab the vols stats for different intervals
open (INFILE,"$input") or die "Could not open sample.txt: $!";
my $date_time;
my $length;
my $col_1;
my $col_2;
my $col_3;
my $col_4;
foreach my $line (<INFILE>)
{
    if ($line =~ m/start/)
        {
            my @date_fields = split(/\s+/,$line);
            $date_time = $date_fields[1];
        }
    if ($line =~ m/length/i)
        {
            my @length_fields = split(/\s+/,$line);
            $length = $length_fields[1];
        }
    if ($line =~ m/0[xX][0-9a-fA-F]+/)
        {
            my @volume_fields = split(/,/,$line);
            $col_1 += $volume_fields[1];
            $col_2 += $volume_fields[2];
            $col_3 += $volume_fields[3];
            $col_4 += $volume_fields[4];
        }
    if ($line =~ /^$/)
        {
            print "$date_time $col_1 $col_2 $col_3 $col_4\n";
            $col_1=0;$col_2=0;$col_3=0;$col_4=0;
        }
}
print "$date_time $col_1 $col_2 $col_3 $col_4\n";
close (INFILE);


__END__

1 33 36 39 42
2 123 126 129 132
3 213 216 219 222