1

以下のプログラムは、次のデータを出力します。

 Wed,Jun,13,10:37:34,2012,759,41,0,30,10,0,0,1
 Wed,Jun,13,10:38:34,2012,767,33,0,25,6,0,0,2
 Wed,Jun,13,10:39:34,2012,758,42,0,32,10,0,0,0
 Wed,Jun,13,10:40:35,2012,758,42,0,29,11,0,0,2
 Wed,Jun,13,10:41:35,2012,761,39,0,34,5,0,0,0
 Wed,Jun,13,10:42:35,2012,769,31,0,22,6,0,0,3
 Wed,Jun,13,10:43:35,2012,754,46,0,29,17,0,0,0

5 分間隔ごとに最大値 (例: 769) を出力する必要があります。理想的には、これは 10:00:00 ~ 10:05:00 などになります。時刻は軍時 (24 時間) です。これを行う最善の方法は何ですか?私はPerlの初心者です。以下は私のコードです:

#!/usr/bin/perl

# This program displays the max thread count at 5 minute intervals and writes the lines to a CSV file.

use strict;
use warnings;
use diagnostics;

# Initialize functions
my @data;
my $line;
my @L1;
#my $outFivemin = "log_5min.csv";
#open (FiveMin, ">> $outFivemin");

# Open the error_log 
open(FH, "error_log");
@data = <FH>;

# Filter the results to MPMStats only
sub findLines {
    my @return = ();
    foreach $line (@data) {
        if ( ($line =~ /notice/) && ($line =~ /rdy/) ) {  
                $line =~ s/ /,/g;   
                my @L1 = split(/|notice|\[|,mpmstats:,|\t|rdy,|bsy,|rd,|wr,|ka,|log,|dns,|cls,/, $line);
                $line =~ s/|notice|\[|,mpmstats:,|\t|rdy,|bsy,|rd,|wr,|ka,|log,|dns,|cls,//g;                   
                push @return, join("", @L1);
        }
    }
    return @return;
}

# Initializers for my data
my($dayOfWeek1,$month1,$dayOfMonth1,$time,$year1,$rdy,$bsy,$rd,$wr,$ka,$log,$dns);
my($cls);

# Create a 2D array
my @L2 = &findLines;
foreach my $line (@L2){
    ($dayOfWeek1, $month1, $dayOfMonth1, $time, $year1, $rdy, $bsy, $rd, $wr, $ka, $log, $dns, $cls) = split(/,/, $line);
    print "$dayOfWeek1,$month1,$dayOfMonth1,$time,$year1,$rdy,$bsy,$rd,$wr,$ka,$log,$dns,$cls";
}
4

4 に答える 4

4

各レコードの日付/時刻を操作して 5 分のキーを提供し、各キーの最大値を維持することをお勧めします。

たとえば、レコードが開始するWed,Jun,13,10:37:34,2012場合、適切なキーはJun 13 10:35 2012です。

通常、これはハッシュになりますが、出力が時系列で必要になる可能性が高く、ソート可能な日付/時刻文字列を提供するには追加の作業とモジュールが必要になるため、以下のプログラムではペアの配列を使用します。

このプログラムは、時刻 (4 番目) フィールドで正規表現置換を使用して機能しs///、分と秒を時刻の前の最初の 2 桁の分に置き換えます。秒は無視され、分は 5 の倍数に切り捨てられます。

配列が空の場合、または別の にいる場合は、新しい[$range, $value]ペアが配列にプッシュされます。それ以外の場合、新しい最大値が見つかった場合、最新のペアの要素が更新されます。@maxima$range$value

このプログラムは、コマンド ラインでログ ファイル名を想定しており、デフォルトerror_logで [なし] が指定されていることに注意してください。

use strict;
use warnings;

@ARGV = ('error_log') unless @ARGV;

my @maxima;

while (<>) {

  my @fields = /([^,\s]+)/g;
  next unless @fields;
  $fields[3] =~ s|(\d+):\d\d$|5*int($1/5)|e;

  my $range = join ' ', @fields[1..4];
  my $value = $fields[5];

  if (@maxima == 0 or $range ne $maxima[-1][0]) {
    push @maxima, [$range, $value];
  }
  else {
    $maxima[-1][1] = $value if $maxima[-1][1] < $value;
  }
}

for (@maxima) {
  printf "Maximum for five minutes starting %s is %d\n", @$_;
}

出力

Maximum for five minutes starting Jun 13 10:35 2012 is 767
Maximum for five minutes starting Jun 13 10:40 2012 is 769

アップデート

5 分間ごとにフィールド 6 の最大値を含むレコード全体が必要であることを理解したので、この改訂されたコードを作成しました。

@L2また、ファイルから読み取るのではなく、配列の内容からも機能します。

ループ内のファイルから読み取り、そこから直接出力を生成する方がはるかに優れていると確信していますがwhile、ログ ファイル データを表示しない限り、これよりも優れた代替案を提案することはできません。

@L2このプログラムは、独自のプログラムに入力した時点から続きます。

my @L2 = findLines();

my @maxima;

for my $record (@L2) {

  my @fields = $record =~ /([^,\s]+)/g;
  next unless @fields;

  my @range = @fields[1..4];
  $range[2] =~ s|(\d+):\d\d$|5*int($1/5)|e;
  my $range = join ' ', @range;
  my $value = $fields[5];

  if (@maxima == 0 or $range ne $maxima[-1][0]) {
    push @maxima, [$range, $value, $record];
  }
  else {
    @{$maxima[-1]}[1,2] = ($value, $record) if $maxima[-1][1] < $value;
  }
}

print $_->[2] for @maxima;

出力

 Wed,Jun,13,10:38:34,2012,767,33,0,25,6,0,0,2
 Wed,Jun,13,10:42:35,2012,769,31,0,22,6,0,0,3
于 2012-06-28T16:36:16.903 に答える
3

これらの線に沿った何かがうまくいくはずです...

#!/usr/bin/perl

use strict;
use warnings;
use 5.010;

# Somewhere to store the data
my %data;

# Process the input a line at a time
while (<DATA>) {
  # Split the input line on commas and colons.
  # Assign the bits we need to variables.
  my ($mon,$day,$hr,$min,$sec,$yr,$val) = (split /[,:]/)[1 .. 7];

  # Normalise the minute value to five-minute increments
  # i.e 37 becomes 35, 42 becomes 40
  $min = int($min / 5) * 5;

  # Create push the value onto an array that is stored in %data using
  # a key generated from the timestamp.
  # Note that we use the 5-min normalised value of the minute so that
  # all values from the same five minute period end up in the same array.
  push @{$data{"$yr-$mon-$day $hr:$min"}}, $val;
}

# For each key in the array (i.e. each five minute increment...
foreach (sort keys %data) {
  # ... sort the array numerically and grab the last element
  # (which will be the largest)
  my $max = (sort { $a <=> $b } @{$data{$_}})[-1];
  # Say something useful
  say "$_ - $max";
}

__DATA__
Wed,Jun,13,10:37:34,2012,759,41,0,30,10,0,0,1
Wed,Jun,13,10:38:34,2012,767,33,0,25,6,0,0,2
Wed,Jun,13,10:39:34,2012,758,42,0,32,10,0,0,0
Wed,Jun,13,10:40:35,2012,758,42,0,29,11,0,0,2
Wed,Jun,13,10:41:35,2012,761,39,0,34,5,0,0,0
Wed,Jun,13,10:42:35,2012,769,31,0,22,6,0,0,3
Wed,Jun,13,10:43:35,2012,754,46,0,29,17,0,0,0
于 2012-06-28T16:02:25.243 に答える
-1

おっと、あなたの csv 出力が解析中のデータ ファイルであると誤解しました。

以下の回答は無視してください。

元のカンマ区切りの行を出力するソリューションを次に示します。最大値と時間も印刷できます。しかし、代わりに結果を含むカンマ区切りのファイルを作成しました。:-)

#!/usr/bin/perl
use strict;
use warnings;
use Text::CSV_XS;

my %interval;
my $csv = Text::CSV_XS->new ({ binary => 1 }) or
     die "Cannot use CSV: ".Text::CSV_XS->error_diag ();

open my $fh, "<", "o33.txt" or die "o33.txt: $!";
while (my $row = $csv->getline ($fh)) {
    my ($time, $amt) = @$row[3,5];
    my ($hr, $min) = split /:/, $time;
    my $key = sprintf "%02d:%02d", $hr, int($min/5) * 5;

    if (exists $interval{$key}) {
        if ($interval{$key}{amt} < $amt) {
            $interval{$key}{amt} = $amt;
            $interval{$key}{data} = $row;
        }
    }
    else { # first time in this 5 minute interval
        $interval{$key}{amt} = $amt;
        $interval{$key}{data} = $row;
    }
}
$csv->eof or $csv->error_diag ();
close $fh or die $!;;


$csv->eol ("\r\n");
open $fh, ">", 'junk.csv' or die $!;

for my $time (sort keys %interval) {
    $csv->print($fh, $interval{$time}{data});
}

close $fh or die $!;

「junk.csv」への出力は次のとおりです。

Wed,Jun,13,10:38:34,2012,767,33,0,25,6,0,0,2
Wed,Jun,13,10:42:35,2012,769,31,0,22,6,0,0,3
于 2012-06-28T22:12:30.080 に答える
-1

これは機能します(?)、(テストしませんでした)、そして の直後のループから始まりますmy @L2 = &findLines

my %interval;
my %month;
@month{qw/ jan feb mar apr may jun jul aug sep oct nov dec /} = '01' .. '12';

# Create a 2D array 
my @L2 = &findLines;
foreach my $line (@L2){ 
    #($dayOfWeek1, $month1, $dayOfMonth1, $time, $year1, $rdy, $bsy, $rd, $wr, $ka, $log, $dns, $cls) = split(/,/, $line); 
    #print "$dayOfWeek1,$month1,$dayOfMonth1,$time,$year1,$rdy,$bsy,$rd,$wr,$ka,$log,$dns,$cls"; 
    my ($dow, $mon, $day, $hr, $min, $sec, $yr, $amt) = split /[:,]/, $line, 9;
    my $key = sprintf "%4d-%02d-%02d %02d:%02d",
                $yr, $month{lc $mon}, $day, $hr, int($min / 5) * 5;

    if (exists $interval{$key}) {
        if ($interval{$key}{amt} < $amt) {
            $interval{$key}{amt} = $amt;
            $interval{$key}{data} = [split ",", $line];
        }
    }
    else { # first time in this 5 minute interval
        $interval{$key}{amt} = $amt;
        $interval{$key}{data} = [split ",", $line];
    }
} 

my $csv = Text::CSV_XS->new ({ binary => 1 }) or
     die "Cannot use CSV: ".Text::CSV_XS->error_diag ();

$csv->eol ("\r\n");
open my $fh, ">", 'junk.csv' or die $!;

for my $time (sort keys %interval) {
    $csv->print($fh, $interval{$time}{data});
}

close $fh or die $!;

これにより、問題の適切な解決策に近づくことを願っています。
更新:分割する最初のフィールドを追加し、8 から 9 の部分に変更しました。

于 2012-06-29T02:33:12.997 に答える