【问题标题】:Suspected Perl buffering issue怀疑 Perl 缓冲问题
【发布时间】:2013-03-31 13:24:22
【问题描述】:

我认为我遇到了 Perl 缓冲问题,因为我需要读取和解析大文本文件(由我自己在前面的代码行中创建)以最终在另一个文件中打印内容。

在某些时候,在读取一个包含 90,855 行的文件和第二个文件的其他一些文件后,脚本并没有完全读取文件的一行。

我已经计算了在这种情况发生之前读取的字符数:233,467,因此在读取文件的下一行之前尝试刷新缓冲区并休眠。它不起作用。

有什么建议吗?

这是我的代码:

foreach $i (@files) {

    my $buff = 0;

    print "Analyzing $i\n";
    sleep(1);
    $program = $1 if $i =~ /(\w+)_SITES/;

    open(FIL, $i) or die "$!: $i\n";
    while (<FIL>) {

        $buff += length($_);
        if ($buff >= 230000) {  #FLUSH THE BUFFER, NOT WORKING!!!
            $buff = 0;
            sleep(1);
            select((select(FIL), $| = 1)[0]);
        }

        undef($a);
        unless ($. == 1) {
            if ($o == 0) {
                if (/^\d+\t(\S+)\t(\S+)\t(\d+)\t(\d+)\t(\S+)\t(\S+)\t(.*)/) {
                    $mirna  = $1;
                    $target = $2;
                    $start  = $3;
                    $end    = $4;
                    $site   = $5;
                    $comp_p = $6;
                    $a      = $7;
                    $j      = "${mirna}_${target}_${start}_$end";
                    $site_nu{$j} = "$mirna\t$target\t$start\t$end\t$site\t$comp_p";    # Store each site in a hash
                }
                else {   #DIES HERE!!!
                  die "$buff characters, in line $.:$_\n"
                }
            }
            else {
                if (/^\d+\t(\S+)\t(\S+)\t(\d+)\t(\d+)\t(\S+)\t(.*)/) {
                    $mirna       = $1;
                    $target      = $2;
                    $start       = $3;
                    $end         = $4;
                    $site        = $5;
                    $a           = $6;
                    $j           = "${mirna}_${target}_${start}_$end";
                    $site_nu{$j} = "$mirna\t$target\t$start\t$end\t$site";    # Store each site in a hash
                }
            }

它死在“DIES HERE!!”在读取第二个文件的 3,413 个字符后死亡。

这是因为正则表达式不起作用,因为只有一半的行在 $_ 中。

【问题讨论】:

  • 这个脚本是单线程的吗?
  • 变量$| 控制在每个print 语句之后自动刷新输出文件。它对输入文件没有影响,刷新输入文件是没有意义的。
  • 如果您尝试读取制表符分隔的文件,您可以查看Text::CSV。您还应该将use strict; use warnings; 添加到此脚本并修复错误/警告。然后使用my 关键字重写代码以将所有变量的范围缩小到尽可能小。

标签: perl buffering


【解决方案1】:

问题几乎可以肯定是因为数据不在要读取的文件中。

您说该文件是从您的代码的早期部分生成的。我怀疑你有一个缓冲问题那里。一旦你的代码完成了文件的写入,使用close 将剩余的数据刷新到文件中,我猜一切都会好起来的。

你应该检查你的close调用的成功状态,像这样

close FILEHANDLE or die "Unable to close temporary file: $!";

除此之外,对如此少量的数据使用临时文件而不是简单地将其全部保存在内存中是否明智值得怀疑。另外:

  • 您必须始终use strictuse warnings 并使用my 声明所有变量,使其尽可能接近它们的第一个使用点。除非您选择在程序顶部声明所有内容(这是一个非常糟糕的主意),否则您没有这样做

  • 您对变量名的选择不稳定。 $i 获取文件名?和$o 为 - 呃 - 什么? $buff 会很好,只是它是一个名义缓冲区的 size 而不是缓冲区 istelf

  • 您应该使用具有open 的三参数形式的词法文件句柄:open my $fil, '&lt;', $i or die "$!: $i";

  • 1234563为此,您需要在代码开头使用 use IO::Handle,除非您运行的是 Perl 5 版本 14 或更高版本,它会按需加载 IO::File(因此是 IO::Handle
  • 我认为简单的split /\t/ 会比您使用的正则表达式更好。看起来像%site_nu 这样$site_nu{$j} = [$mirna, $target, $start, $end, $site, $comp_p] 的数组散列会更好

  • die 字符串的末尾添加换行符会阻止 perl 显示有关源文件和数据文件以及行号的信息,这在您调试时可能很有用

  • 你会做你自己,以及那些你寻求帮助的人,通过很好地格式化你的源代码来帮个忙。如果没有适当的缩进,很难分辨代码块的开始和结束位置

【讨论】:

  • 感谢 cmets,伙计们。是的,我使用严格并使用警告,用我声明变量并在完成打印后成功关闭所有文件。我已经检查过,数据确实在要读取的文件中。我会尝试所有其他建议,我会尽快回复您。非常感谢。
  • 大家好。最后,在开始解析文件并解决问题之前,我刷新了我正在使用的输出文件句柄。非常感谢大家的帮助。
  • @dannyjmh:那么您不可能正确关闭它。关闭文件比刷新文件更好。您还必须在程序开始时在一个大块中声明您的变量,这不是一个好主意。
猜你喜欢
  • 2018-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-12
  • 2015-10-30
相关资源
最近更新 更多