使用Perl在文本文件中对一列数字求和

时间:2012-11-01 23:48:20

标签: perl

好的,所以我对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";

这是数据在文件中的显示方式。这些只是其中的一小部分。我需要在第三列中获取数字。

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

6 个答案:

答案 0 :(得分:15)

您的计划非常接近工作。通过这些更改,它将完全符合您的要求

  • 始终在程序开头使用use strictuse warnings,并使用my声明所有变量。这将帮助您找到许多您可能忽略的简单错误

  • 使用词法文件句柄,open的三参数格式,始终检查任何open调用的返回状态

  • 在循环外声明$total变量。在循环中声明它意味着每次循环都会创建和销毁它并且它将无法累积总数

  • 以相同的方式声明$count变量。你需要它来计算平均值

  • 使用while (FILE) {...}只测试FILE为真。您需要从中进行阅读,因此您必须使用readline

  • <FILE>运算符
  • 您希望默认调用split(不带任何参数),它会将$_中的所有非空格字段作为列表返回

  • 您需要在作业中添加变量以允许每行AMPM字段

以下是对代码的修改,可以正常使用

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

答案 1 :(得分:13)

很有可能使用Perl的awk - 就像自动分割选项一样。有5列;三,包含日期和时间信息,然后是大小,然后是名称。

我编写的脚本的第一个版本也是最详细的:

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

-a(自动拆分)选项会将空白行上的一行拆分为数组@F。结合-n选项(使Perl在一个循环中运行,依次读取文件名参数,或标准输入,而不打印每一行),代码添加$F[3](第四列,计数从0)到$total,在首次使用时自动初始化为零。它还计算$num中的行数。读取所有输入时执行END块;它使用printf()来格式化值。 + 0.0确保算术以浮点运算,而不是整数运算。这与awk脚本非常相似:

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

程序的初稿很少是最优的 - 或者至少,我不是一个好的程序员。修改有帮助。

Perl的设计部分是awk杀手。仍然有一个与Perl一起分发的程序a2p,用于将awk脚本转换为Perl(还有s2p用于将sed脚本转换为Perl)。 Perl确实有一个自动(内置)变量,可以跟踪读取的行数。它有几个名字。最简洁的是$.;如果您在脚本中use English;,则可以使用助记符名称$NR; $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。而这样做的好处在于,您不必使用split和数组以及自己的读取循环; Perl为您完成大部分工作。 (当然,它确实会破坏空输入;这个修复很简单;见下文。)

推荐版本

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

if $.测试读取的行数是否为零;如果printf为零,则省略$.和除法,因此如果没有输入,脚本将不输出任何内容。


有一种名为“Code Golf”的高贵(或卑鄙)游戏在Stack Overflow的早期出现很多,但Code Golf问题不再被视为好问题。 Code Golf的目标是编写一个程序,以尽可能少的字符执行特定任务。您可以使用此功能播放Code Golf并进一步压缩它,如果您不太担心输出格式并且至少使用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$.}'

然而,这并不像推荐版本那样清晰。

答案 2 :(得分: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

答案 3 :(得分: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;

答案 4 :(得分:0)

此解决方案打开文件并循环遍历文件的每一行。然后,它通过拆分1个或多个空格将文件拆分为行中的五个变量。

  • 打开文件进行阅读"<",如果失败,则引发错误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";`
    

答案 5 :(得分: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 / $。将给出平均值。