2010-01-07 73 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似乎是錯誤的循環成語。