组合散列的键以输出(散列的外部连接)
combine keys of hashes for output (outer join of hashes)
我正在使用 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 = ;
my $what = ;
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版本对于实际问题来说并不是那么重要,至少我希望如此...
使用单个哈希不是更容易吗?
#!/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)。)
首先按 天,然后按 模式 构建数据会更容易。这可以使用哈希引用来完成。
use strict;
use warnings;
my %matches;
while ( my $line = <DATA> ) {
if ($line =~ m/^(\d{4}-\d{2}-\d{2})(.+)$/) {
my $day = ;
my $what = ;
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 to output it (though I suggest using 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
。
程序可以进一步简化以使用任意模式。如果您不关心输出中模式的顺序,请包含一个 header,以后您可以轻松添加更多模式。
我为模式使用了配置散列,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 = ;
my $what = ;
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。
我正在使用 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 = ;
my $what = ;
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版本对于实际问题来说并不是那么重要,至少我希望如此...
使用单个哈希不是更容易吗?
#!/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)。)
首先按 天,然后按 模式 构建数据会更容易。这可以使用哈希引用来完成。
use strict;
use warnings;
my %matches;
while ( my $line = <DATA> ) {
if ($line =~ m/^(\d{4}-\d{2}-\d{2})(.+)$/) {
my $day = ;
my $what = ;
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 to output it (though I suggest using 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
。
程序可以进一步简化以使用任意模式。如果您不关心输出中模式的顺序,请包含一个 header,以后您可以轻松添加更多模式。
我为模式使用了配置散列,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 = ;
my $what = ;
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。