【问题标题】:How to write this better in perl如何在 perl 中更好地编写它
【发布时间】:2012-03-02 16:17:43
【问题描述】:

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

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

【问题讨论】:

  • 不是 Perl,而是 sed 's/:.*//' | uniq -c

标签: perl optimization coding-style hash


【解决方案1】:

你所拥有的已经很短了。您可以通过使用词法文件句柄并检查 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

【讨论】:

  • 整洁! - 如果没有人发布更好的内容,将接受您的回答。
  • 您可以使用爱斯基摩吻运算符来代替END}{ say ... keys %t'.
  • 不确定它是否更好/更短/更具可读性,但这里有一个在冒号和爱斯基摩运算符上使用自动拆分:perl -F: -lanwe '$a{$F[0]}++; }{ print "$_ -&gt; $a{$_}" for keys %a' infile
【解决方案2】:

您可以有效地利用自 5.10 版以来的新功能 named capture groups,让您的模式更好地表达您的意图并产生正确排序的输出。

您可以完全放弃数字并创建命名捕获组。符号是 (?&lt;name&gt;...) 声明和 \g{name} 引用。 (为了与 .NET 正则表达式兼容,\g{name} 也可以写成 \k{name}\k&lt;name&gt;\k'name'。)name 不能以数字开头,也不能包含连字符。当同一模式中的不同组具有相同的名称时,对该名称的任何引用都假定最左侧定义的组。命名组以绝对编号和相对编号计算,因此也可以通过这些数字来引用。 (可以使用命名的捕获组来做一些需要(??{}) 的事情。)

捕获组的内容是动态范围的,并且在模式之外可供您使用,直到封闭块结束或直到下一次成功匹配,以先到者为准。 (请参阅 perlsyn 中的复合语句。)您可以通过绝对数字引用它们(使用 $1 而不是 \g1 等);或通过%+ hash 命名,使用$+{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

【讨论】:

    【解决方案3】:

    如果重要的话,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;
    

    【讨论】:

      猜你喜欢
      • 2020-11-25
      • 1970-01-01
      • 1970-01-01
      • 2016-04-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多