0

我正在使用 Perl 5.8.8 分析日志文件。[1]我正在寻找公开两种触发模式中的一些的日子,可能是其中一种,也可能是两者(我在下面显示的代码片段中更改了实际模式)。我对每天的出现次数感兴趣,下一步将是制作一个电子表格,这就是为什么使用制表符进行输出格式化的原因。

因为一天中可能只出现一种模式,所以我需要一种方法来组合两个哈希的键。我通过生成一个新的哈希来做到这一点。有内置功能吗?我搜索了网络和堆栈溢出,但没有任何结果,我在这里得到的唯一结果是Build a string from 2 hashes,但在这种情况下,密钥集是相同的。

#!/usr/bin/perl -w
use strict;
use warnings;
use locale;

# input analysis: searching for two patterns:
my %pattern_a = ();
my %pattern_b = ();
foreach my $line (<>) {
    if ($line =~ m/^(\d{4}-\d{2}-\d{2})(.+)$/) {
        my $day = $1;
        my $what = $2;
        if ($what =~ m/beendet/) {
            $pattern_a{$day} ++;
        } elsif ($what =~ m/ohne/) {
            $pattern_b{$day} ++;
        }
    }
}

# generate the union of hash keys:        <-- In Question
my %union = ();
$union{$_} = 1 for keys %pattern_a;
$union{$_} = 1 for keys %pattern_b;

# formatted output sorted by day:
foreach my $day (sort keys %union) {
    print join "\t", $day, 
            ($pattern_a{$day} || 0), 
            ($pattern_b{$day} || 0)."\n";
}

预期的输出如下所示:

2017-02-01      0       1
2017-02-18      0       592
2017-02-19      2       0

[1]我知道这个 Perl 版本已经过时了。但是我很少使用 Perl,但是当我使用时,它必须运行得很快。因此,弄清楚 Perl 版本等等会在以后完成。但是 Perl 版本对于实际问题并不那么重要,至少我希望如此......

4

2 回答 2

2

使用单个哈希不是更容易吗?

#!/usr/bin/perl
use strict;
use warnings;

my %stats;

while (my $line = readline) {
    my ($day, $pattern) = $line =~ /^(\d{4}-\d{2}-\d{2}).*(beendet|ohne)/
        or next;

    $stats{$day}{$pattern}++;
}

for my $day (sort keys %stats) {
    printf "%s\t%d\t%d\n",
        $day,
        $stats{$day}{beendet} // 0,
        $stats{$day}{ohne} // 0;
}

如果您使用的是 5.10 之前的 perl,请替换//||; 在这种情况下,它没有有效的区别。(但考虑升级:5.8.8 是从 2006 年开始的。现在已经有十多年的历史了。官方维护的 perl 版本是 5.22(2015)和 5.24(2016)。)

于 2017-02-23T11:25:42.203 回答
1

首先按day构建数据,然后按pattern构建数据更容易。这可以使用哈希引用来完成。

use strict;
use warnings;

my %matches;
while ( my $line = <DATA> ) {
    if ($line =~ m/^(\d{4}-\d{2}-\d{2})(.+)$/) {
        my $day = $1;
        my $what = $2;
        if ($what =~ m/beendet/) {
            $matches{$day}->{a} ++;
        } elsif ($what =~ m/ohne/) {
            $matches{$day}->{b} ++;
        }
    }
}

# formatted output sorted by day:
foreach my $day (sort keys %matches) {
    print join(
        "\t",
        $day,
        $matches{$day}->{a} || 0,
        $matches{$day}->{b} || 0,
    ), "\n";
}

__DATA__
2017-02-01 einmal Pommes ohne
2017-02-02 Wartung gestartet
2017-02-02 Wartung beendet
2017-02-03 ohne Moos nix los

该程序产生如下输出

2017-02-01  0   1
2017-02-02  1   0
2017-02-03  0   1

要了解数据结构,您可以使用Data::Dumper来输出它(尽管我建议改用Data::Printer,因为它是供人类使用的,而不是作为序列化的)。

use Data::Dumper;
print Dumper \%matches;
__END__

$VAR1 = {
          '2017-02-03' => {
                            'b' => 1
                          },
          '2017-02-02' => {
                            'a' => 1
                          },
          '2017-02-01' => {
                            'b' => 1
                          }
        };

如您所见,数据首先按日期构建。每个键代表一天。在里面,有一个额外的哈希引用,它只保存一个键。这就是模式。后来我们先迭代一天。然后我们得到

{
    'b' => 1
}

在第一次迭代中。然后我们迭代所有的模式。上面的程序不是通过实际迭代,而是通过显式声明每个可能的键来做到这一点。如果它在那里,它就会被使用。如果未定义,则0使用||运算符设置为。


该程序可以进一步简化为使用任意模式。如果您不关心输出中模式的顺序,请包含一个标题,您以后可以轻松添加更多模式。

我对模式使用了配置哈希,并使用Text::Table创建输出。

use strict;
use warnings;
use Text::Table;

my %matches;
my %patterns = (
    beendet => qr/beendet/,
    ohne    => qr/ohne/,
    komplex => qr/foo\sbar?/, # or whatever
);
while ( my $line = <DATA> ) {
    if ($line =~ m/^(\d{4}-\d{2}-\d{2})(.+)$/) {
        my $day = $1;
        my $what = $2;
        foreach my $name ( sort keys %patterns ) {
            if ( $what =~ $patterns{$name} ) {
                $matches{$day}->{$name}++ ;
                last;
            }
        }
    }
}

# formatted output sorted by day:
my @head = sort keys %patterns;
my $tb = Text::Table->new( 'Tag', @head );

foreach my $day (sort keys %matches) {
    $tb->load([ $day, map { $matches{$day}->{$_} || 0 } @head ]);
}

print $tb;

__DATA__
2017-02-01 einmal Pommes ohne
2017-02-02 Wartung gestartet
2017-02-02 Wartung beendet
2017-02-03 ohne Moos nix los

这打印

Tag        beendet komplex ohne
2017-02-01 0       0       1   
2017-02-02 1       0       0   
2017-02-03 0       0       1   

如果您不想安装其他模块,也许只需创建一个 CSV 文件。由于您来自德国,我建议使用分号;作为分隔符,因为德语 Excel 使用它作为默认分隔符。

这是一个详细的示例,说明如何执行此操作,而不是 Text::Table。

my @head = sort keys %patterns;
print join( ';', @head ), "\n";
foreach my $day (sort keys %matches) {
    my @cols;
    push @cols, $matches{$day}->{$_} || 0 for @head;
    print join ';', $day, @cols;
    print "\n";
}

输出是

beendet;komplex;ohne
2017-02-01;0;0;1
2017-02-02;1;0;0
2017-02-03;0;0;1

但是,如果您不希望它出现在屏幕上,您也应该查看Text::CSV 。

于 2017-02-23T11:29:10.007 回答