如何在perl中更好地编写这个

时间:2012-03-02 16:17:43

标签: perl optimization coding-style hash

给定一个大的输入文件,如下所示:

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的功能,我很确定这可以用更少的行完成。 如果你能提供一些例子,我将不胜感激。我希望它对那些希望减少为实现某些目的而编写的代码行的人有用。

3 个答案:

答案 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;