我有一个CSV文件,其中包含不同行中的重复项目。
x1,y1
x2,y2
y1,x1
x3,y3
包含x1,y1
和y1,x1
的两行是匹配的,因为它们以不同的顺序包含相同的数据。
我需要你的帮助才能找到一个在12MB文件中搜索这些行的算法。
答案 0 :(得分:1)
如果你可以在字段之间定义一些排序和相等关系,你可以存储一个规范化的表格,并测试你的行与之相等。
例如,我们将对您的字段使用字符串比较,但是在对它们进行小写后。然后我们可以根据这种关系对部件进行排序,并通过嵌套哈希创建一个查找表:
use strict; use warnings;
my $cache; # A hash of hashes. Will be autovivified later.
while (<DATA>) {
chomp;
my @fields = split;
# create the normalized representation by lowercasing and sorting the fields
my @normalized_fields = sort map lc, @fields;
# find or create the path in the lookup
my $pointer = \$cache;
$pointer = \${$pointer}->{$_} for @normalized_fields;
# if this is an unknow value, make it known, and output the line
unless (defined $$pointer) {
$$pointer = 1; # set some defined value
print "$_\n"; # emit the unique line
}
}
__DATA__
X1 y1
X2 y2
Y1 x1
X3 y3
在此示例中,我使用标量1
作为查找数据结构的值,但在更复杂的情况下,原始字段或行号可以存储在此处。为了示例,我在此处使用了空格分隔值,但您可以通过调用split
或其他内容来替换Text::CSV
。
这种散列哈希方法具有次线性空间复杂度,最坏情况下线性空间复杂度。查找时间仅取决于记录中字段的数量(和大小),而不取决于记录总数。
限制:所有记录必须具有相同数量的字段,或者某些较短记录可能被错误地视为“已查看”。为了避免这些问题,我们可以使用更复杂的节点:
my $pointer = \$cache;
$pointer = \$$pointer->[0]{$_} for @normalized_fields;
unless (defined $$pointer->[1]) {
$$pointer->[1] = 1; ...
}
或为非必要字段引入默认值(例如原始文件的分隔符)。这里有一个NUL字符的例子:
my $fields = 3;
...;
die "record too long" if @fields > $fields;
...; # make normalized fields
push @normalized_fields, ("\x00") x ($fields - @normalized_fields);
...; # do the lookup
答案 1 :(得分:1)
很大程度上取决于您在找到重复行后想要了解的内容。该程序使用简单的哈希来列出那些等效的行的行号。
use strict;
use warnings;
my %data;
while (<DATA>) {
chomp;
my $key = join ',', sort map lc, split /,/;
push @{$data{$key}}, $.;
}
foreach my $list (values %data) {
next unless @$list > 1;
print "Lines ", join(', ', @$list), " are equivalent\n";
}
__DATA__
x1,y1
x2,y2
y1,x1
x3,y3
<强>输出强>
Lines 1, 3 are equivalent
答案 2 :(得分:0)
A
和B
x
和y
,将每个用作键,将另一个用作两个哈希表的值(例如$A->{x} = y; $B->{y} = x;
)A
或B
的键存在 - 如果存在,则表示您具有反向匹配 - 如果不存在,则重复步骤3中的添加过程将其添加到哈希表答案 3 :(得分:0)
要在没有哈希表的情况下执行amon答案的版本,如果您的数据是数字的,您可以:
sort
这样做的好处是使用的内存少于哈希表,但可能需要更长的时间来处理。
答案 4 :(得分:0)
#! /usr/bin/perl
use common::sense;
my $re = qr/(?!)/; # always fails
while (<DATA>) {
warn "Found duplicate: $_" if $_ =~ $re;
next unless /^(.*),(.*)$/;
die "Unexpected input at line $.: $_" if "$1$2" =~ tr/,//;
$re = qr/^\Q$2,$1\E$|$re/
}
__DATA__
x1,y1
x2,y2
y1,x1
x3,y3