【问题标题】:perl manipulate with big filesperl 操作大文件
【发布时间】:2015-05-31 03:34:10
【问题描述】:

我正在处理一个 16GB 的文件和一个小文件。

我尝试将这两个文件加载到内存中。然后,我在大文件的每一行上移动并验证小文件中的某些内容(对于我在小文件上迭代的大文件中的每一行)。

这是我的代码

local $/ = undef;
open my $fh1, '<', $in or die "error opening $in: $!";
my $input_file = do { local $/; <$fh1> };

local $/ = undef;
open my $fh2, '<', $handle or die "error opening $handle: $!";
my $handle_file = do { local $/; <$fh2> };

my $counter_yes = 0;
my $counter_no  = 0;
my $flag        = 0;

my @lines1 = split /\n/, $input_file;

foreach my $line( @lines1 ) {

    my @f = split('\t', $line); # $f[0] and $f[1]
    print "f0 and f1 are: $f[0] and $f[1]\n";
    my @lines2 = split /\n/, $handle_file;

    foreach my $input ( @lines2 ){

        #print "line2 is: $input\n";
        my @sp = split /:/, $input; # $sp[0] and $sp[1]

        if ( $sp[0] eq $f[0] ){

            my @r = split /-/, $sp[1];

            if ( ($f[1] >= $r[0]) && ($f[1] <= $r[1]) ){
                $flag = 1;
                $counter_yes = $counter_yes;
                last;
            }
        }
    }

    if ( $flag == 0 ){
        $counter_no = $counter_no  ;
    }
}

当我运行它时,我得到了错误

Split loop at script.pl line 30, <$fh2> chunk 1

可能是什么原因?

【问题讨论】:

  • 您可以尝试添加使用字节;脚本顶部的编译指示。可能是编码问题。
  • 把你的代码放在你的问题中。 ...另外,我为你的数据哭泣。

标签: perl bigdata


【解决方案1】:

我正在处理一个 16GB 的文件和一个小文件。

我尝试将这两个文件加载到内存中。

你有 16GB 的内存吗?实际上,您的代码需要超过 32GB 的内存。

在 script.pl 第 30 行,块 1 处拆分循环

我无法复制该错误。 Perl 错误通常是非常具有描述性的,但甚至无法理解。

接下来,如果你的代码中有这个:

my $x = 10;
#nothing changes $x
#in these
#lines
$x = 10;

最后一行的目的是什么?然而,你这样做了:

$/ = undef;
#Nothing changes $/
#in these lines
$/ = undef;

接下来,所有 perl 程序都应该以下列行开头:

<guess>

如果你不知道,那么你需要买一本 perl 入门书。

【讨论】:

    【解决方案2】:

    你为什么要把整个文件读成一个大字符串,然后把它分成一个行数组,而你本来可以把它读成一个行数组呢?你为什么要一遍又一遍地为第二个文件做呢?你可以

    chomp(my @lines1 = <$fh>);
    chomp(my @lines2 = <$fh2>);
    

    在你的程序的顶部并消除$input_file$handle_file 否则将不会被使用,以及所有$/ 的废话。这很可能是问题的根源,因为错误消息表明 split 正在生成“太多”字段。

    【讨论】:

      【解决方案3】:

      您可以运行perldoc perldiag 来了解一些内置错误和警告的含义。

         Split loop
             (P) The split was looping infinitely.  (Obviously, a split
             shouldn't iterate more times than there are characters of input,
             which is what happened.)  See "split" in perlfunc.
      

      你要拆分的字符串太大了,Perl 认为它在无限迭代。当 Perl 拆分字符串的次数超过字符串长度 + 10 次时,假设它处于无限循环中,它会给出此错误。不幸的是,它将该数字存储为一个 32 位整数,最多只能容纳 20 亿个并且可以更改。您的字符串超过 160 亿,因此结果将无法预测。

      这是 5.20 中的 recently fixed 以及处理超过 2G 大小的字符串的许多其他相关问题。因此,如果您升级 Perl,您的代码将“工作”。

      但是,您的代码效率极低,并且会破坏大多数机器的内存,导致它在交换到磁盘时非常慢。至少你应该只吃小文件并逐行读取 16 gig 文件。

      my @small_data = <$small_fh>;
      chomp @small_data;
      
      while( my $big = <$big_fh> ) {
          chomp $big;
      
          for my $small (@small_data) {
              ...
          }
      }
      

      但即使这样也会非常低效,如果您的小文件包含 1000 行,那么该循环将运行 16 万亿次!

      由于您似乎正在检查大文件中的条目是否在小文件中,因此最好将小文件中的条目转换为哈希表。

      my %fields;
      while( my $line = <$small_fh> ) {
          chomp $line;
          my @sp = split /:/, $line;
          $fields{$sp[0]} = $sp[1];
      }
      

      现在您可以遍历大文件并进行哈希查找。

      while( my $line = <$big_fh> ) {
          chomp $line;
          my @f = split('\t', $line);
      
          if( defined $fields{$f[0]} ) {
              ...
          }
      }
      

      【讨论】:

      • 感谢您的回答!那么是否需要将文件加载到字符串中?或者对于另一个问题,$small 和 $big_fh 是文件句柄还是字符串?
      • @user687459: 任何以fh 结尾的都可能是一个文件句柄,所以my @small_data = &lt;$small_fh&gt; 将整个小文件读入数组,每个元素一行。
      猜你喜欢
      • 2012-05-29
      • 2011-09-14
      • 1970-01-01
      • 1970-01-01
      • 2012-08-29
      • 2017-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多