代码之家  ›  专栏  ›  技术社区  ›  Ryan C. Thompson

如何在Perl中读取、分析、然后“取消读取”并重新读取输入流的开头?

  •  1
  • Ryan C. Thompson  · 技术社区  · 14 年前

    我正在读取和处理来自 Perl中的ARGV文件句柄(即 while(<>) 一个常规的文件句柄,可以是STDIN。但是,我需要分析输入的很大一部分,以便检测四种不同但极为相似的格式(FASTQ质量分数的不同ASCII编码;请参见 here ). 一旦我决定了数据的格式,我就需要回去第二次解析这些行来实际读取数据。

    所以我需要读两遍前500行左右的内容。或者,换个角度看,我需要先读前500行,然后“放回去”,这样我才能再读一遍。因为我可能在读《标准》杂志,所以我不能只回到开头。而且文件很大,所以我不能把所有东西都读入内存(尽管把前500行读入内存是可以的)。最好的方法是什么?

    或者,我可以以某种方式复制输入流吗?

    等一下。我刚刚意识到我不能再将输入作为一个大流来处理,因为我必须独立地检测每个文件的格式。所以我不能用ARGV。不过,问题的其余部分仍然存在。

    2 回复  |  直到 14 年前
        1
  •  2
  •   cjm    14 年前

    如您所说,如果文件句柄可能是STDIN,则不能使用 seek

    my @lines;
    
    while (<$file>) {
      push @lines, $_;
      last if @lines == 500;
    }
    
    ... # examine @lines to determine format
    
    while (defined( $_ = @lines ? shift @lines : <$file> )) {
      ... # process line
    }
    

    记住,你需要一个明确的 defined 在这种情况下,因为 对某些人 while 循环不适用于此更复杂的表达式。

        2
  •  1
  •   cjm    14 年前

    CPAN module unread 方法 IO::Handle 上课。然而,它的警告让人有些谨慎。我会仔细评估它的适用性。

    如果您真的只需要保存500行(每行都很短),那么这个模块就足够了;它的示例确实使用了 STDIN

    <> 运算符导致打开和读取多个不同的文件,然后我不知道您将能够备份到与当前打开的文件不同的文件。

    标准物质 .

    if (@ARGV == 0 && -t STDIN) {
        # select one or the other of the next two lines:
    
        # opt 1: emit warning 
        warn "$0: reading stdin from /dev/tty\n";
    
        # opt 2: populate @ARGV
        @ARGV = grep { -f && -T } <*>;  # glob plain textfiles
    
     }
    

    在上面的第二种情况中,默认为当前目录中的所有纯文本文件,还应该决定如果 grep 生成空列表。

    @ARGV "." 相反,这样程序默认为进程的当前工作目录。