根据第一列查找两个大文件之间的相似点和不同点

时间:2016-11-09 21:58:01

标签: perl hash

我有两个以制表符分隔的文件,超过一百万行,我需要根据第一列查找有多少值是常见的,有多少是特定于其中一个文件。

我正在尝试使用以下代码在Perl中执行此操作,但它无法正常工作。

我需要考虑给定文件大小的计算时间。

有人可以帮我纠正这个问题,或建议更有效的方法吗?

left.txt

K00134:78_1 272 1   3057610
K00134:78_0 272 1   3057610
K00134:78_2 272 1   3057610
K00134:78_3 272 1   3057610

right.txt

K00134:78_1 272 1   3057610
K00134:78_5 272 1   3057610
K00134:78_6 272 1   3057610
K00134:78_3 272 1   3057610

Perl代码

use strict;
use warnings;

my %Set;

open (SET1, "<", "left.txt") or die "cannot open file";

while (<SET1>) {
    my @line = split (/\t/, $_);
    $Set{$line[0]} = $line[1];
}

my @k = keys %Set;
foreach my $key (@k) {
    print "$key, $Set{$key}\n";
}
close SET1;

open (SET2, "<", "right.txt") or die "cannot open file";
print "common:\n";

while (<SET2>) {
    chomp;

    if ( exists $Set{"$_"} ) {
        print "$Set{$_}\n";
    }
}

close SET2;

输出应如下所示,列出基于第一列的公共字段 -

common lines - 
K00134:78_1 272 1   3057610
K00134:78_3 272 1   3057610

不常见的行 - left.txt

K00134:78_0 272 1   3057610
K00134:78_2 272 1   3057610

不常见的行 - right.txt

K00134:78_5 272 1   3057610
K00134:78_6 272 1   3057610

另外,我也试图从每个文件中添加不匹配作为输出,但我不确定它是否可能给定文件的大小。谢谢!

1 个答案:

答案 0 :(得分:2)

您的第二个读取循环代码错误。它应该通过制表符分开并检查。将其更改为:

while (<SET2>) {
    my @line = split (/\t/, $_);
    print $_ if exists $Set{$line[0]};
}

它会起作用。你的方法还可以。由于您只想比较第一列,因此您无需将$ Set {}的值设置为第二列($ line [1]),您只需将其设置为&#39;&# 39;为了节省记忆。另外,为了节省内存,请确保left.txt是两者中最小的一个。这是一个有效的例子:

use strict;
use warnings;

my %Set;

open (SET1, "<", "left.txt") or die "cannot open file";

while (<SET1>) {
    my @line = split (/\t/, $_);
    $Set{$line[0]} = '';
}

close SET1;

open (SET2, "<", "right.txt") or die "cannot open file";
print "common:\n";

while (<SET2>) {
    my @line = split (/\t/, $_);
    print $_ if exists $Set{$line[0]};
}

close SET2;

编辑 - 这是另一种可以提供你想要的方法

use strict;
use warnings;

my %Set;

sub readFile {
    my ($fn, $bit) = @_;
    open (F, "<" ,$fn) or die "can't open file";
    while (<F>) {
        my ($k) = split (/\t/, $_);
        $Set{$k} = $Set{$k} || [0, $_];
        $Set{$k}[0] |= $bit;
    }
    close F;
}

sub showByBit {
    my ($k, $bit) = @_;
    foreach my $key (@{$k}) {
        my $a = $Set{$key};
        print $a->[1] if $a->[0] == $bit;
    }
}

readFile('left.txt', 1);
readFile('right.txt', 2);
my @k = keys %Set;

print "common lines -\n";
showByBit(\@k, 3);

print "uncommon lines - left.txt\n";
showByBit(\@k, 1);

print "uncommon lines - right.txt\n";
showByBit(\@k, 2);