首页 > 解决方案 > 文件句柄第二次读取失败

问题描述

如何foreach在 Perl 的循环中第二次从文件句柄中读取?

foreach $a (@b){
    while(my $line = <IN>){
        if($line = /$a/){
            print $line;
        }
    }
}

上面的代码没有处理 list 中的第二个元素@b。如何使它成为可能?

标签: regexperl

解决方案


你的内部循环,while(my $line = <IN>)从句柄中提取行,IN直到它到达文件的末尾。

当您的外部循环 ,foreach $a (@b)尝试再次读取IN时,它仍处于文件末尾。循环的第一次迭代foreach消耗了文件中的所有行,没有为其他迭代留下任何内容。

有几种可能的方法来解决这个问题:

  • 在尝试IN再次读取之前,请回到开头:

    foreach $a (@b){
        seek IN, 0, 0
            or die "Cannot seek(): $!";
        while (my $line = <IN>) {
            ...
        }
    }
    

    但是,这仅适用于真实文件,不适用于管道、套接字或终端。

  • 预先将整个文件读入内存,然后遍历一个普通数组:

    my @lines = <IN>;
    foreach $a (@b){
        foreach my $line (@lines) {
            ...
        }
    }
    

    但是,如果文件很大,这将占用大量内存。

  • 切换两个循环的顺序:

    while (my $line = <IN>) {
        foreach $a (@b) {
            ...
        }
    }
    

    这是我最喜欢的。现在您只需要从文件中读取一次。@b已经在内存中,因此您可以根据需要多次迭代它。


旁注:

  • 不要使用裸字文件句柄,如IN. 普通变量(例如$IN)在各个方面都非常出色。
  • 不要使用名为$aor的变量$b。它们有点特别,因为 Perl 在sort.
  • 我个人的偏好是永远不要使用< >. 它奇怪地超载(它可以表示readlineglob,具体取决于您使用的确切语法)并且它不是非常直观。使用readline意味着永远不会有任何语法歧义,即使没有 Perl 经验的程序员也能弄清楚它的作用。

随着这些变化:

while (my $line = readline $IN) {
    foreach my $re (@regexes) {
        if ($line =~ /$re/) {
            print $line;
        }
    }
}

推荐阅读