1

我正在从Perl 中的 ARGV 文件句柄(即while(<>)构造)读取和处理一个常规文件句柄的输入流,它可能是 STDIN。但是,我需要分析输入的重要部分,以便检测它以四种不同但极其相似的格式中的哪一种进行编码(FASTQ 质量分数的不同 ASCII 编码;请参见此处)。一旦我决定了数据的格式,我需要返回并再次解析这些行以实际读取数据。

所以我需要读取流的前 500 行左右两次。或者,换个角度来看,我需要阅读前 500 行,然后将它们“放回原处”,这样我就可以再次阅读它们。因为我可能正在阅读 STDIN,所以我不能只是回到开头。而且文件很大,所以我不能只将所有内容读入内存(尽管将前 500 行读入内存是可以的)。最好的方法是什么?

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

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

4

2 回答 2

2

正如你所说,如果文件句柄可能是标准输入,你不能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添加隐式的特殊情况不适用于这个更复杂的表达式。definedwhile

于 2010-10-30T18:39:21.203 回答
1

有一个该类提供方法的CPAN 模块。然而,它的警告让人有些谨慎。我会仔细评估它的适用性。unreadIO::Handle

如果您真的只需要节省 500 行,每行都相当短,那么该模块可能就足够了;它的例子确实使用STDIN.

但是,我对魔法 ARGV 感到紧张。如果您的<>操作员导致打开和读取多个不同的文件,那么我不知道您将能够备份到与当前打开的文件不同的文件。

因此,您最终可能只是自己编写推回逻辑。要么这样,要么对与多个输入文件和/或STDIN.

我的大多数带有魔法 ARGV 处理的程序在开始时都有警卫,内容如下:

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的来"."代替,以便程序默认为进程的当前工作目录。

于 2010-10-30T17:42:02.137 回答