16

さて、私はPerlを初めて使用します。私はテキストファイルを持っていて、そのファイルには4列のデータ(日付、時刻、ファイルのサイズ、ファイル)があります。ファイルを開いてファイルの平均サイズを取得できる小さなスクリプトを作成する必要があります。私はオンラインでたくさん読んだことがありますが、それでもその方法がわかりません。これは私がこれまでに持っているものですが、私がこれを正しく行うことにさえ近いかどうかはわかりません。

#!/usr/bin/perl

open FILE, "files.txt";
#@array = File;

while(FILE){
    #chomp;

    ($date, $time, $numbers, $type) = split(/ /,<FILE>);

    $total += $numbers;

}
print"the total is $total\n";

これは、データがファイル内でどのように見えるかです。これらはそれらのほんの一部です。3列目の数字を取得する必要があります。

12/02/2002  12:16 AM              86016 a2p.exe
10/10/2004  11:33 AM               393 avgfsznew.pl
11/01/2003  04:42 PM             38124 c2ph.bat
4

6 に答える 6

16

あなたのプログラムはかなり機能に近いです。これらの変更により、それはあなたが望むことを正確に行います

  • プログラムの開始時に常にuse strictとを使用し、を使用use warningsしてすべての変数を宣言しますmy。それはあなたがそうでなければ見落とすかもしれない多くの単純なエラーを見つけることによってあなたを助けます

  • の3パラメータ形式である字句ファイルハンドルを使用し、呼び出しの戻りステータスを常に確認openますopen

  • $totalループの外側で変数を宣言します。ループ内で宣言すると、ループの周りで毎回作成および破棄され、合計を累積できなくなります。

  • $count同じ方法で変数を宣言します。平均を計算するために必要になります

  • while (FILE) {...}本当のテストだけを使用しFILEます。代わりにそれを読み取る必要があるため、次のreadlineような演算子を使用する必要があります<FILE>

  • スペース以外のすべてのフィールドをリストとして返すデフォルトの呼び出し(パラメーターなし)が必要ですsplit$_

  • AM各行にatheまたはPMfieldを許可するには、割り当てに変数を追加する必要があります

これが正常に機能するコードの変更です

use strict;
use warnings;

open my $fh, '<', "files.txt" or die $!;

my $total = 0;
my $count = 0;

while (<$fh>) {

    my ($date, $time, $ampm, $numbers, $type) = split;

    $total += $numbers;
    $count += 1;

}

print "The total is $total\n";
print "The count is $count\n";
print "The average is ", $total / $count, "\n";

出力

The total is 124533
The count is 3
The average is 41511
于 2012-11-02T00:29:35.027 に答える
14

Perlのawkような自動分割オプションを使いたくなります。5つの列があります。3つには日付と時刻の情報が含まれ、次にサイズ、次に名前が含まれます。

私が書いたスクリプトの最初のバージョンも、最も冗長です。

perl -n -a -e '$total += $F[3]; $num++; END { printf "%12.2f\n", $total / ($num + 0.0); }'

-a自動分割)オプションは、空白の行を配列に分割します@F-nオプション(各行を出力せずにファイル名引数を順番に読み取るループでPerlを実行する、または標準入力を実行する)と組み合わせると、コードは$F[3](0から数えて4番目の列)をに追加します$total。これは自動的に初期化されます。最初の使用ではゼロ。また、の行をカウントし$numます。ENDブロックは、すべての入力が読み取られたときに実行されます。printf()値をフォーマットするために使用します。は+ 0.0、整数演算ではなく、浮動小数点で演算が行われることを保証します。これはawkスクリプトと非常によく似ています。

awk '{ total += $4 } END { print total / NR }'

プログラムの最初のドラフトが最適になることはめったにありません。少なくとも、私はそれほど優れたプログラマーではありません。改訂が役立ちます。

Perlは、部分的にはawkキラーとして設計されました。a2pスクリプトをPerlに変換するためのPerlで配布されたプログラムがまだありますawk(そしてスクリプトをPerlに変換するためのプログラムもありs2pますsed)。また、Perlには、読み取られた行数を追跡する自動(組み込み)変数があります。いくつかの名前があります。tersestは$.; スクリプトを使用して$NRいる場合は、ニーモニック名を使用できます。use English;です$INPUT_LINE_NUMBER。したがって、使用$numする必要はありません。また、Perlはとにかく浮動小数点除算を行うので、その+ 0.0部分は不要でした。これは次のバージョンにつながります:

perl -MEnglish -n -a -e '$total += $F[3]; END { printf "%12.2f\n", $total / $NR; }'

また:

perl -n -a -e '$total += $F[3]; END { printf "%12.2f\n", $total / $.; }'

あなたの気まぐれや空想に合うように印刷フォーマットを調整することができます。これは基本的に、私が長期的に使用するスクリプトです。それは決して長蛇の列がなくてもかなり明確です。必要に応じて、スクリプトを複数の行に分割することもできます。IMNSHOさん、1行の読みやすさは問題にならないほど簡単な作業です。splitそして、これの美しさは、配列やループを自分でいじくり回す必要がないことです。Perlはそのほとんどをあなたに代わって行います。(確かに、空の入力で爆発します。その修正は簡単です。以下を参照してください。)

推奨バージョン

perl -n -a -e '$total += $F[3]; END { printf "%12.2f\n", $total / $. if $.; }'

読み取られたif $.行数がゼロかどうかをテストします。がゼロの場合、printfおよび除算は省略さ$.れるため、入力がない場合、スクリプトは何も出力しません。


Stack Overflowの初期によくプレイされていた、「コードゴルフ」と呼ばれる高貴な(または無視できる)ゲームがありますが、コードゴルフの質問はもはや良い質問とは見なされていません。コードゴルフの目的は、特定のタスクを可能な限り少ない文字で実行するプログラムを作成することです。これを使用してコードゴルフをプレイし、出力の形式についてあまり心配せず、少なくともPerl 5.10を使用している場合は、さらに圧縮することができます。

perl -Mv5.10 -n -a -e '$total += $F[3]; END { say $total / $. if $.; }'

そして、明らかに、そこには不要なスペースや文字がたくさんあります。

perl -Mv5.10 -nae '$t+=$F[3];END{say$t/$.if$.}'

ただし、これは推奨バージョンほど明確ではありません。

于 2012-11-01T23:57:47.087 に答える
2
#!/usr/bin/perl

use warnings;
use strict;

open my $file, "<", "files.txt";
my ($total, $cnt);
while(<$file>){
        $total += (split(/\s+/, $_))[3];
        $cnt++;
}
close $file;
print  "number of files: $cnt\n";
print  "total size: $total\n";
printf "avg: %.2f\n", $total/$cnt;

または、次を使用できますawk

awk '{t+=$4} END{print t/NR}' files.txt
于 2012-11-01T23:56:58.000 に答える
1

これを試してみてください:

#!/usr/bin/perl -l

use strict; use warnings;

open my $file, '<', "my_file" or die "open error [$!]";

my ($total, $count);

while (<$file>){
    chomp;
    next if /^$/;
    my ($date, $time, $x, $numbers, $type) = split;
    $total += $numbers;
    $count++;
}

print "the average is " . $total/$count . " and the total is $total";

close $file;
于 2012-11-01T23:57:35.833 に答える
0

これと同じくらい簡単です:

perl -F -lane '$a+=$F[3];END{print "The average size is ".$a/$.}' your_file

以下でテスト済み:

> cat temp
12/02/2002  12:16 AM              86016 a2p.exe
10/10/2004  11:33 AM               393 avgfsznew.pl
11/01/2003  04:42 PM             38124 c2ph.bat

今実行:

> perl -F -lane '$a+=$F[3];END{print "The average size is ".$a/$.}' temp
The average size is 41511
> 

説明: -F -aは、行を配列形式で格納することを示します。デフォルトの区切り文字はスペースまたはタブです。したがって、nopw $F[3]はファイルのサイズを示します。すべての行が処理されるまで、4番目の列のすべてのサイズを合計します。ENDは、ファイル内のすべての行を処理した後に実行されます。

だから$。最後に行数を示します。つまり、$ a/$です。平均を与えます。

于 2012-11-02T06:05:22.047 に答える
0

このソリューションはファイルを開き、ファイルの各行をループします。次に、1つ以上のスペースで分割することにより、ファイルを行内の5つの変数に分割します。

  • 読み取り用にファイルを開き、"<"失敗した場合はエラーを発生させますor die "..."
  • my ($total, $cnt)列の合計と追加されたファイルの数はカウントです
  • while(<FILE>) { ... }ファイルハンドルを使用してファイルの各行をループし、その行をに格納します。$_
  • chompの入力レコード区切り文字を削除します$_。UNIXでは、デフォルトの区切り文字は改行です\n
  • split(/\s+/, $_)で表される現在の行を$_区切り文字で分割し\s+ます。\sスペースを表し、+その後は「1つ以上」を意味します。したがって、次の行を1つ以上のスペースで分割します。
  • 次に更新$totalして$cnt

    #!/usr/bin/perl
    
    open FILE, "<", "files.txt" or die "Error opening file: $!";
    my ($total, $cnt);
    
    while(<FILE>){
      chomp;
      my ($date, $time, $am_pm, $numbers, $type) = split(/\s+/, $_); 
      $total += $numbers;
      $cnt++; 
    }
    close FILE;
    
    print"the total is $total and count of $cnt\n";`
    
于 2012-11-02T00:00:24.650 に答える