给定一个大的输入文件,如下所示:
02/26/2012 08:54:38 Error:java.sql.Exception
02/26/2012 08:54:48 Error:java.sql.Exception
02/26/2012 08:56:05 Error:java.sql.Exception
02/26/2012 08:57:21 Error:java.sql.Exception
02/26/2012 08:59:29 Error:java.sql.Exception
02/26/2012 09:01:14 Error:java.sql.Exception
02/26/2012 09:08:48 Error:java.sql.Exception
02/26/2012 09:10:41 Error:java.sql.Exception
我试图找出每小时的错误数;也就是说,我正在寻找一个如下所示的输出文件:
02/26/2012 08 -> 5
02/26/2012 09 -> 3
这是一个适合我的脚本:
#!/bin/perl
open(MYFILE, 'tata2');
my %table;
while (<MYFILE>) {
chomp;
$dtkey = substr $_, 0, 13;
$table{$dtkey}++;
}
close(MYFILE);
for my $key (keys %table) {
print "$key -> $table{$key}\n";
}
但是考虑到Perl的功能,我很确定这可以用更少的行完成。 如果你能提供一些例子,我将不胜感激。我希望它对那些希望减少为实现某些目的而编写的代码行的人有用。
答案 0 :(得分:6)
你所拥有的已经相当短暂。您可以通过使用词法文件句柄并检查open的返回值来改进一些事情。
这是使用Perl的一些其他语法功能的重写:
open my $fh, '<', 'filename' or die $!;
my %table;
while (<$fh>) {
$table{$1}++ if /([^:]+)/ # regex is a bit shorter than the substr
}
print "$_ -> $table{$_}\n" for keys %table; # statement modifier form
或者如果你真的想要它简短,那么一个班轮怎么样:
perl -lnE '$t{$1}++ if /([^:]+)/; END {say "$_ -> $t{$_}" for keys %t}' infile
答案 1 :(得分:2)
您可以有效地使用自{5.10版以来的新功能named capture groups,让您的模式更好地表达您的意图并生成正确的排序输出。
您可以完全省去数字并创建命名捕获组。表示法是
(?<name>...)
来声明和\g{name}
来引用。 (要与.NET正则表达式兼容,\g{name}
也可以写为\k{name}
,\k<name>
或\k'name'
。) name 不得开始带数字,也不含连字符。当同一模式中的不同组具有相同名称时,对该名称的任何引用都将采用最左侧定义的组。命名组以绝对编号和相对编号计数,因此也可以通过这些编号来引用。 (可以使用命名捕获组执行操作,否则需要(??{})
。)捕获组内容是动态范围的,并且可以在模式外部使用,直到封闭块结束或直到下一次成功匹配为止,以先到者为准。 (请参阅perlsyn中的复合语句。)您可以通过绝对数字来引用它们(使用
$1
而不是\g1
等);或使用$+{name}
通过%+
hash按名称命名。
对于每一行输入,查找匹配但将组件置换为YYYY / MM / DD HH顺序以便于排序。
#! /usr/bin/env perl
use strict;
use warnings;
use 5.10.0; # named capture buffers
*ARGV = *DATA; # for demo only; remove for real use
my %hour_errors;
while (<>) {
$hour_errors{"$+{y}/$+{m}/$+{d} $+{h}"}++
if m!^ (?<m> \d+) / (?<d> \d+) / (?<y> \d+) \s+ (?<h> \d+) :!x;
}
print "$_ -> $hour_errors{$_}\n" for sort keys %hour_errors;
__DATA__
02/26/2012 08:54:38 Error:java.sql.Exception
02/26/2012 08:54:48 Error:java.sql.Exception
02/26/2012 08:56:05 Error:java.sql.Exception
02/26/2012 08:57:21 Error:java.sql.Exception
02/26/2012 08:59:29 Error:java.sql.Exception
02/26/2012 09:01:14 Error:java.sql.Exception
02/26/2012 09:08:48 Error:java.sql.Exception
02/26/2012 09:10:41 Error:java.sql.Exception
输出:
2012/02/26 08 -> 5 2012/02/26 09 -> 3
答案 2 :(得分:1)
如果重要的话,Substr比正则表达式更有效。如果您有权访问CPAN,可以使用Tie :: IxHash消除排序,它在内部保持密钥的插入顺序(但仍然是哈希)。 (我添加了几行来说明排序问题。)
use Tie::IxHash;
tie my %table, 'Tie::IxHash';
$table{substr $_, 0, 13}++ while <DATA>;
print "$_ -> $table{$_}\n" for keys %table;
__DATA__
02/26/2012 09:10:41 Error:java.sql.Exception
02/26/2012 08:54:38 Error:java.sql.Exception
02/26/2012 08:54:48 Error:java.sql.Exception
02/26/2012 08:56:05 Error:java.sql.Exception
02/26/2012 08:57:21 Error:java.sql.Exception
02/26/2012 08:59:29 Error:java.sql.Exception
02/26/2012 09:01:14 Error:java.sql.Exception
02/26/2012 09:08:48 Error:java.sql.Exception
02/26/2012 09:10:41 Error:java.sql.Exception
03/26/2012 08:54:38 Error:java.sql.Exception
03/26/2012 08:54:48 Error:java.sql.Exception
03/26/2012 08:56:05 Error:java.sql.Exception
03/26/2012 08:57:21 Error:java.sql.Exception
03/26/2012 08:59:29 Error:java.sql.Exception
03/26/2012 09:01:14 Error:java.sql.Exception
03/26/2012 09:08:48 Error:java.sql.Exception
04/26/2012 08:54:38 Error:java.sql.Exception
04/26/2012 08:54:48 Error:java.sql.Exception
04/26/2012 08:56:05 Error:java.sql.Exception
04/26/2012 08:57:21 Error:java.sql.Exception
04/26/2012 08:59:29 Error:java.sql.Exception
04/26/2012 09:01:14 Error:java.sql.Exception
04/26/2012 09:08:48 Error:java.sql.Exception
04/26/2012 09:10:41 Error:java.sql.Exception
如果您无法访问Tie :: IxHash,那么这里是一个带有一些键的简短版本(减去重复 DATA )。
my %table;
$table{substr $_, 0, 13}++ while <DATA>;
print "$_ -> $table{$_}\n" for sort { "@{[($a=~/(\d+)\D?/g)[2,1,0,3]]}" cmp "@{[($b=~/(\d+)\D?/g)[2,1,0,3]]}" } keys %table;