使用Perl在文件中查找相等的行

时间:2013-06-01 17:08:22

标签: perl

我有一个CSV文件,其中包含不同行中的重复项目。

x1,y1
x2,y2
y1,x1
x3,y3

包含x1,y1y1,x1的两行是匹配的,因为它们以不同的顺序包含相同的数据。

我需要你的帮助才能找到一个在12MB文件中搜索这些行的算法。

5 个答案:

答案 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)

  1. 制作两个哈希表AB
  2. 一次输入一行输入
  3. 对于第一个行xy,将每个用作键,将另一个用作两个哈希表的值(例如$A->{x} = y; $B->{y} = x;
  4. 对于第二个和后续行对,测试第二个字段的值是否作为AB的键存在 - 如果存在,则表示您具有反向匹配 - 如果不存在,则重复步骤3中的添加过程将其添加到哈希表

答案 3 :(得分:0)

要在没有哈希表的情况下执行amon答案的版本,如果您的数据是数字的,您可以:

  1. 逐行输入,通过数字排序对字段1和2进行排序
  2. 在第一个和第二个字段上将结果传递给UNIX sort
  3. 逐行流式地排序输出,检查当前行是否与上一行匹配(报告反向匹配,如果为true)
  4. 这样做的好处是使用的内存少于哈希表,但可能需要更长的时间来处理。

答案 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