2010-01-07 72 views
4

我快速记下了一个Perl脚本,该脚本只用几列数字来平均几个文件。它涉及从文件句柄数组中读取数据。这里是脚本:如何从一个数组元素的Perl文件句柄读取数据?

#!/usr/local/bin/perl 

use strict; 
use warnings; 

use Symbol; 

die "Usage: $0 file1 [file2 ...]\n" unless scalar(@ARGV); 

my @fhs; 

foreach(@ARGV){ 
    my $fh = gensym; 
    open $fh, $_ or die "Unable to open \"$_\""; 
    push(@fhs, $fh); 
} 

while (scalar(@fhs)){ 
    my ($result, $n, $a, $i) = (0,0,0,0); 
    while ($i <= $#fhs){ 
     if ($a = <$fhs[$i]>){ 
      $result += $a; 
      $n++; 
      $i++; 
     } 
     else{ 
      $fhs[$i]->close; 
      splice(@fhs,$i,1); 
     } 
    } 
    if ($n){ print $result/$n . "\n"; } 
} 

这是行不通的。如果我调试脚本,之后我初始化@fhs它看起来像这样:

DB<1> x @fhs 
0 GLOB(0x10443d80) 
    -> *Symbol::GEN0 
     FileHandle({*Symbol::GEN0}) => fileno(6) 
1 GLOB(0x10443e60) 
    -> *Symbol::GEN1 
     FileHandle({*Symbol::GEN1}) => fileno(7) 

到目前为止,一切都很好。但它没有在那里我尝试从文件中读取部分:

DB<3> x $fhs[$i] 
0 GLOB(0x10443d80) 
    -> *Symbol::GEN0 
     FileHandle({*Symbol::GEN0}) => fileno(6) 
    DB<4> x $a 
0 'GLOB(0x10443d80)' 

$一个充满了这个字符串,而不是从水珠读的东西。我做错了什么?

+0

我会给你1000个代表停止使用P ... – 2010-01-07 19:24:02

回答

12

只能使用<>中的简单标量变量从文件句柄读取。 <$foo>作品。 <$foo[0]>不从文件句柄读取;它实际上相当于glob($foo[0])。您必须使用readline内建的临时变量,或使用IO::File和OO符号。

$text = readline($foo[0]); 
# or 
my $fh = $foo[0]; $text = <$fh>; 
# or 
$text = $foo[0]->getline; # If using IO::File 

如果你不删除从循环内的数组元素,你可以很容易地改变你的while循环到foreach循环使用一个临时变量。

个人而言,我认为使用gensym来创建文件句柄是一个丑陋的黑客攻击。你应该使用IO :: File,或者传递一个未定义的变量到open(它至少需要Perl 5.6.0,但现在已经快10年了)。 (只是说my $fh;代替my $fh = gensym;和Perl会自动创建一个新的文件句柄,并将其存储在$fh当你调用open

+6

或者相当于'',它被拼写为'readline HANDLE'。 – 2010-01-07 19:53:49

1

我无法理解你的逻辑。你想阅读几个文件,其中只包含数字(每行一个数字)并打印其平均值?

use strict; 
use warnings; 

my @fh; 
foreach my $f (@ARGV) { 
    open(my $fh, '<', $f) or die "Cannot open $f: $!"; 
    push @fh, $fh; 
} 

foreach my $fh (@fh) { 
    my ($sum, $n) = (0, 0); 
    while (<$fh>) { 
     $sum += $_; 
     $n++; 
    } 
    print "$sum/$n: ", $sum/$n, "\n" if $n; 
} 
+0

问题是文件不能保证具有相同的行数。 – 2010-01-07 19:48:34

+0

为什么这是一个问题? – 2010-01-08 12:33:31

2

如果你愿意用一点神奇的,你可以做到这一点很简单:

use strict; 
use warnings; 

die "Usage: $0 file1 [file2 ...]\n" unless @ARGV; 

my $sum = 0; 

# The current filehandle is aliased to ARGV 
while (<>) { 
    $sum += $_; 
} 
continue { 
    # We have finished a file: 
    if(eof ARGV) { 
     # $. is the current line number. 
     print $sum/$. , "\n" if $.; 
     $sum = 0; 

     # Closing ARGV resets $. because ARGV is 
     # implicitly reopened for the next file. 
     close ARGV; 
    } 
} 

除非你用的是很老的Perl,在摆弄gensym是没有必要的。 IIRC,perl 5.6和更新的版本很满意普通的词法句柄:open my $fh, '<', 'foo';

+1

我喜欢。但是'$ count'是什么? – ephemient 2010-01-07 22:08:40

+0

此外,'除非'将在标量环境中隐式使用'@ ARGV' ...我不会把这算作魔法,这也是我的写法:) – ephemient 2010-01-07 22:15:37

+0

好点。 “除非标量”是粘贴OP的代码而留下的。在我意识到可以使用线路号码之前,$ count已经遗留下来了。 – daotoad 2010-01-07 23:16:48

1

看起来像for循环对于你来说会更好,你可以在那里使用标准的读取(迭代)操作符。

for my $fh (@fhs) { 
    while (defined(my $line = <$fh>)) { 
     # since we're reading integers we test for *defined* 
     # so we don't close the file on '0' 
     #... 
    } 
    close $fh; 
} 

它看起来并不像你想要的快捷循环。因此,while似乎是错误的循环成语。