2012-03-02 11 views
5

Dato un file di grandi dimensioni di input che assomiglia a questo:Come scrivere questo meglio in perl

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 

Sto cercando di scoprire il conteggio degli errori per ora; vale a dire, sto cercando un file di output che assomiglia a questo:

02/26/2012 08 -> 5 
02/26/2012 09 -> 3 

Ecco uno script che sta lavorando per me:

#!/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"; 
} 

Ma dato le caratteristiche del Perl, io sono abbastanza sicuro che questo può essere fatto in meno righe. Apprezzerei molto se è possibile fornire alcuni esempi. Spero che sia utile per coloro che vogliono ridurre le righe di codice scritte per ottenere qualcosa.

+2

'usare rigorosamente; utilizzare gli avvisi; ' – Ether

+3

Non Perl, ma' sed 's /:.*//' | uniq -c' –

risposta

6

Quello che hai è già abbastanza breve. È possibile migliorare un po 'le cose utilizzando gli handle di file lessicali e controllando il valore restituito di open.

Ecco una riscrittura utilizzando alcune delle altre caratteristiche sintattiche del 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 

Oppure, se si vuole veramente breve, come circa un uno di linea:

perl -lnE '$t{$1}++ if /([^:]+)/; END {say "$_ -> $t{$_}" for keys %t}' infile 
+0

pulito! - accetterà la tua risposta se nessuno posterà qualcosa di meglio. –

+3

Invece di 'END', puoi usare l'operatore eskimo kiss. '} {say ... keys% t''. – TLP

+2

Non sono sicuro che sia migliore/più breve/più leggibile, ma eccone uno che utilizza l'autosplit su due punti e l'operatore eskimo: 'perl -F: -lanwe '$ a {$ F [0]} ++; } {stampa "$ _ -> $ a {$ _}" per le chiavi% a 'infile' – TLP

2

È possibile fare un uso efficace di named capture groups, una nuova funzione dalla versione 5.10, per fare in modo che il pattern esprima meglio l'intento e produca correttamente l'output ordinato.

È possibile fare a meno dei numeri e creare gruppi di cattura denominati. La notazione è (?<name>...) da dichiarare e \g{name} da referenziare. (Per essere compatibile con le espressioni regolari .NET, , \k<name> o \k'name'.) nome non deve iniziare con un numero, né contenere trattini. Quando gruppi diversi all'interno dello stesso modello hanno lo stesso nome, qualsiasi riferimento a quel nome assume il gruppo definito più a sinistra. I gruppi con nome contano in numerazione assoluta e relativa, e quindi possono anche essere indicati da quei numeri. (È possibile fare cose con gruppi di cattura denominati che altrimenti richiederebbero (??{}).)

I contenuti dei gruppi di cattura sono definiti dinamicamente e disponibili all'utente all'esterno del pattern fino alla fine del blocco che lo contiene o fino alla successiva corrispondenza corretta, a seconda della condizione che si verifica primo. (Vedi Compound Statements in perlsyn.) È possibile fare riferimento ad essi per numero assoluto (usando $1 invece di \g1, ecc.); o per nome tramite %+ hash, utilizzando $+{name}.

Per ogni riga di input, cercare una partita, ma permutare i componenti da AAAA/ordine MM/DD HH per l'ordinamento facile.

#! /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 

uscita:

2012/02/26 08 -> 5 
2012/02/26 09 -> 3
1

Substr è più efficiente di regex se quello che conta. Se si ha accesso a CPAN, l'ordinamento può essere eliminato con Tie :: IxHash, che mantiene le chiavi internamente nell'ordine di inserimento (ma è ancora un hash). (Ho aggiunto alcune righe in più che illustrano il problema di ordinamento.)

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 

Se non si ha accesso a Tie :: IxHash allora ecco una versione corta con una sorta di chiavi (meno ripetizione DATI).

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; 
Problemi correlati