File di lettura perl un giro tempo
-
29-10-2019 - |
Domanda
Ho un file di testo delimitato nello spazio che sto leggendo e sto cercando di aggiungere dati in base al mese, i dati sembrano questo:
Lun 04 aprile 08:00:00 MDT 2011 120.72 0.3 0.707 25.609 25.609
Lun 04 aprile 07:45:00 MDT 2011 119,94 0.3 0.707 25.443 25.443
Sto cercando di sommare i totali mensili:
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
use vars;
my $line;
my @data;
my @months;
my ($day, $month, $date, $time, $gmt, $year, $volt, $amp, $pf, $watt, $voltamp,
$voltsum, $wattsum, $count, $months, $monthlytotal );
$voltsum = 0;
$wattsum = 0;
open(DATAFILE, "@ARGV") || die $!;
@months = qw( Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec );
while (<DATAFILE>) {
$line = $_;
chomp $line;
@data = split(/\s/,$line);
$day = $data[0];
$month = $data[1];
$date = $data[2];
$time = $data[3];
$gmt = $data[4];
$year = $data[5];
$volt = $data[6];
$amp = $data[7];
$pf = $data[8];
$watt = $data[9];
$voltamp = $data[10];
Voglio abbinare il mese, aggiungere i miei dati e stampare il risultato una volta, ma il mio controllo del flusso è sbagliato, qualche idea su come farlo correttamente?
Voglio leggere ogni riga, provare quale mese è, aggiungere tutti i mesi simili insieme e restituire il risultato.
foreach $months(@months) {
if ( $months =~ $month ) {
$voltsum += $voltamp;
$wattsum += $watt;
print "$month $year $wattsum $voltsum\n";
}
elsif ( $months !~ $month ) {
$voltsum = 0;
$wattsum = 0;
}
}
}
close (DATAFILE);
# print "Month Year Watts Vars\n" ;
# print "--------------------------\n";
# print " $months $month $year $wattsum $voltsum\n\n";
Soluzione
Potresti trarre vantaggio dall'uso di un modulo per analizzare il tuo timestamp. Tuttavia, una soluzione semplice potrebbe essere quella di fare qualcosa del genere:
my %sum;
while (<>) { # instead of open on @ARGV, just use diamond operator
my ($day, $month, $date, $time, $gmt, $year, $volt, $amp,
$pf, $watt, $voltamp) = split;
$sum{"$year$month"}{'voltamp'} += $voltamp;
$sum{"$year$month"}{'watt'} += $watt;
....
}
Non esattamente impermeabile, ma potrebbe soddisfare le tue esigenze. Quindi puoi semplicemente estrarre i dati del mese con
for my $month (keys %sum) {
print "voltamp sum ($month): $sum{$month}{'voltamp'}\n";
....
}
Altri suggerimenti
La seguente struttura dei dati potrebbe essere più appropriata (Perldoc Perldsc)
use warnings;
use strict;
use Data::Dumper;
$Data::Dumper::Sortkeys = 1;
my %sums;
while (<DATA>) {
my @tokens = split;
$sums{$tokens[1]}{volt } += $tokens[6];
$sums{$tokens[1]}{amp } += $tokens[7];
$sums{$tokens[1]}{pf } += $tokens[8];
$sums{$tokens[1]}{watt } += $tokens[9];
$sums{$tokens[1]}{voltamp} += $tokens[10];
}
print Dumper(\%sums);
__DATA__
Mon Apr 04 08:00:00 MDT 2011 120.72 0.3 0.707 25.609 25.609
Mon Apr 04 07:45:00 MDT 2011 119.94 0.3 0.707 25.443 25.443
Stampe:
$VAR1 = {
'Apr' => {
'amp' => '0.6',
'pf' => '1.414',
'volt' => '240.66',
'voltamp' => '51.052',
'watt' => '51.052'
}
};
Se vuoi farlo in streaming - come sembra il tuo programma -, supponendo che i tuoi dati siano ordinati, dovresti prima introdurre una variabile per controllare il mese, che consiste in un mese+anno. Questo è più sicuro (aprile 2011 non è aprile 2012).
- Inizializza il "ultimo mese controllato"
- Somma Init dove necessario e riassunto.
- Stampa quando è il momento di stampare
Potrebbe sembrare:
my $last_month;
print "Month Year Watts Vars\n";
print "--------------------------\n";
while (<DATAFILE>) {
# ... snip
$voltamp = $data[10];
my $current_month = $month . ' ' . $year; # Apr 2011 != Apr 2012
if (!defined $last_month || $current_month ne $last_month) {
# month changed
if (defined $last_month) {
# print intermediate sum
print "$last_month $wattsum $voltsum\n";
}
$last_month = $current_month;
$voltsum = 0;
$wattsum = 0;
}
$voltsum += $voltamp;
$wattsum += $watt;
}
print "$last_month $wattsum $voltsum\n"; # print last sum
Tuttavia, preferirei anche riassumere in un hash come le recenti 2 risposte che sono emerse durante la scrittura, quindi salto la mia seconda soluzione.