我快速记下了一个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)'
$ a填充了这个字符串,而不是从glob中读取的东西。我做错了什么?
答案 0 :(得分: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
。)
答案 1 :(得分: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';
答案 2 :(得分: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;
}
答案 3 :(得分: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
似乎是错误的循环习语。