在Perl中获取file2中没有出现的所有行的最快方法是什么?

时间:2009-04-30 20:37:32

标签: perl algorithm

我有两个(非常大的)文本文件。在运行时方面,最快的方法是创建第三个文件,其中包含未出现在file2中的所有file1行?

因此,如果file1包含:

Sally  
Joe  
Tom  
Suzie

file2包含:

Sally  
Suzie  
Harry  
Tom

然后输出文件应包含:

Joe

6 个答案:

答案 0 :(得分:13)

创建一个包含文件2中每一行的hashmap。然后,对于文件1中的每一行,如果它不在hashmap中,则输出它。这将是O(N),这是您可以实现的最佳效率等级,因为您必须阅读输入。

Perl实现:

#!/usr/bin/env perl
use warnings;
use strict;
use Carp ();

my $file1 = 'file1.txt';
my $file2 = 'file2.txt';

my %map;
{
    open my $in, '<',$file2 or Carp::croak("Cant open $file2");
    while (<$in>) {
      $map{$_} = 1;
    }
    close($in) or Carp::carp("error closing $file2");
}
{
   open my $in,'<', $file1 or Carp::croak("Cant open $file1");
   while (<$in>) {
    if (!$map{$_}) {
      print $_;
    }
   }
   close $in or Carp::carp("error closing $file1");
}

如果文件2太大以至于hashmap不适合内存,那么我们手头有一个不同的问题。然后可能的解决方案是在文件2的块上使用上述解决方案(小到足以装入内存),将结果输出到临时文件。如果文件1和文件2之间有足够的匹配,那么总输出应该是合理的大小。为了计算最终结果,我们在临时文件中执行行的交集,即对于最终结果中的行,它必须出现在每个临时文件中。

答案 1 :(得分:7)

不是Perl但有效:

diff <(sort file1) <(sort file2)

答案 2 :(得分:5)

我很惊讶没人提出最节省内存的方法,即分别对两个文件进行排序,然后执行列表合并。如果您使用的是Unix,以下3个简单命令可以快速执行您所需的操作:

sort < file1 > file1.sorted
sort < file2 > file2.sorted
comm -2 -3 file1.sorted file2.sorted > differences

如果文件太大以至于Perl必须将VM分页以将所有行加载到哈希表中,这种技术将更多更快。否则,哈希表 - 基于方法应该更快。

如果您使用的是Unix,最好使用系统的外部sort命令,因为它对内存使用情况很敏感 - 使用Perl的sort()需要将文件的全部内容读入内存。

如果您使用的是Windows,请注意提供的sort命令不区分大小写且无法关闭!此外,没有comm命令在Windows上,所以你需要自己动手 - 用上面的第三行替换:

perl subtract_sets.pl file1.sorted file2.sorted > differences.txt

subtract_sets.pl

#!/usr/bin/perl
open my $f1, '<', $ARGV[0] or die;
open my $f2, '<', $ARGV[1] or die;

my $x = <$f1>;
my $y = <$f2>;

while (defined $x && defined $y) {
    if ($x lt $y) {
        print $x;
        $x = <$f1>;
    } elsif ($y lt $x) {
        print $y;
        $y = <$f2>;
    } else {
        # Lines match
        $x = <$f1>;
        $y = <$f2>;
    }
}

while (defined $x) {
    print $x;
    $x = <$f1>;
}

while (defined $y) {
    print $y;
    $y = <$f2>;
}

答案 3 :(得分:4)

#!/usr/bin/perl                                                                 

use warnings; 
use strict;

open(my $alpha, '<', 'file1') || die "Couldn't open file1";
open(my $beta, '<' , 'file2') || die "Couldn't open file2";

my %data;
map {$data{$_} = 1} <$alpha>;
map {print $_ unless $data{$_}} <$beta>;

答案 4 :(得分:2)

对你来说有什么特别的?比你的RAM更大?您的基本答案是使用哈希值,如果文件大于您的RAM,那么您需要使用hash tied to a dbm

答案 5 :(得分:2)

只是一些效率基准:

10k行,每行最多10个字符的随机字符串。

          Rate slipset marcog
slipset 47.6/s      --   -16%
marcog  56.7/s     19%     --

100k行,每行最多10个字符的随机字符串。

          Rate slipset marcog
slipset 3.02/s      --   -34%
marcog  4.60/s     52%     -

1000k行,每行最多10个字符的随机字符串。

        s/iter slipset marcog
slipset   4.09      --   -33%
marcog    2.75     49%     --

1k行,每行最多100个字符的随机字符串。

         Rate  slipset marcog
slipset 379/s      --   -12%
marcog  431/s     14%     --

100k行,每行最多100个字符的随机字符串

          Rate slipset  marcog
slipset 2.15/s      --    -30%
marcog  3.08/s     44%      --

1k行,每行最多1000个字符的随机字符串

         Rate slipset  marcog
slipset 133/s      --    -10%
marcog  148/s     11%      --

100k行,每行最多1000个字符的随机字符串

          Rate slipset  marcog
slipset 1.01/s      --    -18%
marcog  1.22/s     22%      --

内存效率

Marcog:100k行,每行最多1000个字符的随机字符串:

Memory usage summary: heap total: 163_259_635, heap peak: 61_536_800, stack peak: 17_648
         total calls     total memory   failed calls
 malloc|     307_425      162_378_090              0
realloc|       1_461           96_878              0  (nomove:1_218, dec:1_026, free:0)
 calloc|      12_762          784_667              0
   free|     307_598      155_133_460

Slipset:100k行,每行最多1000个字符的随机字符串:

Memory usage summary: heap total: 647_103_469, heap peak: 118_445_776, stack peak: 17_648
         total calls     total memory   failed calls
 malloc|     508_089      186_752_811              0
realloc|     399_907      459_553_775              0  (nomove:334_169, dec:196_380, free:0)
 calloc|      12_765          796_883              0
   free|     507_584      256_315_688