在Perl中,如何在一个循环中读取多个文件句柄?

时间:2012-07-12 15:47:38

标签: perl file-io

我想知道如何在Perl中实现它:

while ( not the end of the files )
    $var1 = read a line from file 1
    $var2 = read a line from file 2
    # operate on variables
end while

我不确定如何在一个while循环中从两个文件一次读取一行。

3 个答案:

答案 0 :(得分:11)

好像你自己写的答案差不多了。只需检查两个文件句柄的eof,如下所示:

while (not eof $fh1 and not eof $fh2) {
    my $var1 = <$fh1>;
    my $var2 = <$fh2>;
    # do stuff
}

更多阅读:

答案 1 :(得分:10)

注意:我扩展了我的回答以回应@zostay和@ jm666的评论。

为这个问题提出一个有效,清晰,简洁的答案的第一步,从相关variables go in an aggregate的想法开始。因此,数组@fh将包含我们同时读取的文件句柄。

然后,我们可以从每个文件句柄中读取一行,并使用<>运算符和map将它们存储在一个数组中。 map采用转换规则和列表,并返回另一个列表。因此:

my @lines = map scalar <$_>, @fh;

获取@fh中的文件句柄,并从每个文件句柄中读取一行(注意scalar),并将这些行放在@lines中。这是one-to-one的{​​{1}}转换。

正如@fh的文档所示,如果到达文件末尾,<>将返回未定义的值,或者出现错误。

现在,检查我们是否成功读取所有文件的一种方法是检查数字defined行是否与文件句柄的数量相同。 grep选择满足特定条件的列表元素。因此

<>

将检查@fh == grep defined, my @lines = map <$_>, @fh; 中的文件句柄数是否与@fh中定义的元素数相同。但是,此比较两侧出现的@lines确实令人困惑,因此检查@fh中没有未定义元素的另一种方法是:

@lines

如果你想把这个条件放在while循环中,你必须写:

0 == grep !defined, my @lines = map <$_>, @fh;

如果你选择until,你可以简单地写一下:

while (0 == grep !defined, my @lines = map <$_>, @fh) {

这意味着“直到至少有一条读取线返回未定义的值,执行循环体”。

现在,请注意Perl's eofC's eof不同。 Perl's eof的文档指出:

  

实用提示:您几乎不需要在Perl中使用until (grep !defined, my @lines = map <$_>, @fh) { ,因为输入操作符通常会在数据耗尽或遇到错误时返回eof

如果每次循环都检查undef,那么你的文件IO就会翻倍,因为“这个函数实际上是在读取一个字符,然后是eof它的。”

我几乎总是用我的代码给出一个自包含的runnable示例。下面,我不想依赖系统上现有的任何特定文件,因此我使用始终可用的ungetcDATA句柄。与使用STDIN函数相反,当您使用此方法时,您不必担心从哪里读取:您关心的是任何一个文件的readline是否返回undefined值。它也可以与任意数量的文件句柄一起使用。另外,你真的没有把文件句柄放在一个数组中,但正如我所说,相关的变量属于一个聚合,所以如果你发现自己输入类似

的东西
eof

意识到you should have used an array to store the filehandles

my $var1 = <$fh1>;
my $var2 = <$fh2>;

#!/usr/bin/env perl use strict; use warnings; my @fh = (\*DATA, \*STDIN); until (grep !defined, my @lines = map scalar <$_>, @fh) { print for @lines; } __DATA__ one two three 中的行用尽时,此示例脚本将停止询问您在STDIN上的输入。如果脚本中没有任何尾随空白行,则必须在脚本终止之前输入 three 四行。

现在,如果您想知道哪些文件句柄到达目的地,您将切换到使用类似的内容:

DATA

重要

上面的循环用于在任何一个文件耗尽时停止。另一方面,您可能希望循环运行,直到所有文件都用完为止。在这种情况下,您可以使用:

#!/usr/bin/env perl

use strict; use warnings;

my @fh = (\*DATA, \*STDIN);

while (1) {
    my @lines = map scalar <$_>, @fh;

    if (my @eof = grep !defined($lines[$_]), 0 .. $#fh) {
        warn "Could not read from filehandle(s) '@eof'";
        last;
    }

    print for @lines;
}

__DATA__
one
two
three

答案 2 :(得分:2)

没有明确eof()检查的另一个简单解决方案是这样的:

while (defined(my $var1 = <$fh1>) and defined(my $var2 = <$fh2>)) {
    # do stuff
}

这使用<>返回undef的事实,如果&amp;只有当你在档案的最后。