如何更好地使用Perl编写此代码

5

给定一个大型的输入文件,长这样:

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
不是 Perl,而是 sed 's/:.*//' | uniq -c。 (注解:该命令用于在 Unix 或 Linux 系统中统计文件中每个唯一的行前面出现的重复行数,其中 sed 's/:.*//' 用于截取以冒号为分隔符的第一个字段,uniq -c 用于计数并输出唯一行的数量和相应的计数。) - Keith Thompson
3个回答

6

您已经拥有了一个相当简短的内容。您可以通过使用词法文件句柄并检查打开的返回值来稍微改进一下。

以下是使用一些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

很好! - 如果没有其他更好的答案,我会接受您的回答。 - ring bearer
3
你可以使用“爱斯基摩亲吻”运算符代替“END”,写作}{ say ... keys %t' - TLP
2
不确定是否更好/更短/更易读,但这里有一个使用冒号和爱斯基摩运算符的自动分割的示例:perl -F: -lanwe '$a{$F[0]}++; }{ print "$_ -> $a{$_}" for keys %a' infile - TLP

2
您可以充分利用自版本 5.10 以来的命名捕获组这一新功能,使您的模式更好地表达您的意图并产生正确排序的输出。
您可以完全不使用数字,而是创建命名捕获组。表示法为(?<name>...)来声明和\g{name}来引用。(为了与.NET正则表达式兼容,\g{name}也可以写成\k{name}\k<name>\k'name'。)name不能以数字开头,也不能包含连字符。当同一模式中的不同组具有相同的名称时,对该名称的任何引用都假定为最左边定义的组。命名组计数在绝对和相对编号中,因此也可以通过这些数字进行引用。(使用命名捕获组可以做一些否则需要使用(??{})的事情。)
捕获组内容具有动态作用域,并且在封闭块的结束或下一个成功匹配之前在模式外部可用。(请参见perlsyn中的复合语句。)您可以通过绝对编号(使用$1而不是\g1等)或通过%+哈希使用名称引用它们,使用$+{name}
对于每一行输入,寻找匹配项并按照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

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;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接