Perl合并两个文件并删除重复项 - 令人困惑的问题

时间:2012-12-04 23:26:13

标签: perl merge text-files

我目前正在尝试在perl脚本中合并两个不同的文本文件 - 但它有点复杂。

问题(为了便于解释而略有改动):

我有两个不同的文本文件,一个名为dog1.txt,一个名为dog2.txt(如下所示)。

dog1.txt
    poodle     8888
    jackrussel    5743
    beagle     6784

dog2.txt 
    spaniel    9843
    poodle    3756
    germanshepard    3267
    beagle    3478  

正如你所看到的那样,poodle和beagle包含在两个文本文件中,但是有不同的四位数代码。

我想要的是创建的新文件将这两个文件合并在一起如果有任何重复项,例如poodle和beagle,我希望新文件包含与poodle和beagle关联的四位数来自dog1 .txt文件而不是dog2.txt。

新文件需要看起来像这样(与狗名称的顺序无关,它与它们相关的数字需要正确):

final_dog.txt
    poodle    8888
    germanshepard    3267
    jackrussel    5743
    beagle    6784
    spaniel    9843

我尝试了许多不同的解决方案,但没有一种能够按照我需要的方式可靠地工作。

非常感谢任何帮助,谢谢

3 个答案:

答案 0 :(得分:4)

您基本上想要打印遇到的第一个实例。因此,您可以使用标准惯用法来删除重复项。

perl -lane'print if !$seen{$F[0]}++' dog1.txt dog2.txt >final_dog.txt

此方法使用最少的内存。它也会尽可能早地开始输出(如果您输出输出,则非常有用)。


要满足新要求,请使用

perl -lane'print if @F==2 && $F[1]=~/^\d+\z/ && !$seen{$F[0]}++' \
   dog1.txt dog2.txt >final_dog.txt

答案 1 :(得分:0)

作为一个单行:

perl -MData::Dumper -lwe '
           $d = pop;             # save filename for later
           %d = map split, <>;   # process dog1.txt
           push @ARGV, $d;       # put the second file name back
           while (<>) {          # add new entries, unless already defined
               my ($dog,$num) = split; $d{$dog} //= $num; 
           } 
           print Dumper \%d' dog1.txt dog2.txt

<强>输出:

$VAR1 = {
          'poodle' => '8888',
          'spaniel' => '9843',
          'germanshepard' => '3267',
          'beagle' => '6784',
          'jackrussel' => '5743'
        };

此解决方案使用菱形运算符@ARGV<>中的参数进行隐式打开。 //=已定义或赋值运算符不会覆盖已定义的值。

正如ikegami巧妙地指出的那样,通过反转参数可以消除检查值的必要性。然后这变得非常简单:

perl -MData::Dumper -lwe '
           %d = map split, <>; 
           print Dumper \%d' dog2.txt dog1.txt   # note reversed args

我会将print语句留给您,因为您没有指定您的文件是否为制表符分隔符或其他内容。但你可能会这样做:

print join "\t", $_, $d{$_} for keys %d;      # tab separated
printf "%-20s %s\n", $_, $d{$_} for keys %d;  # fixed width

请注意,这是一种破坏性的解决方案,与ikegami的答案不同,后者保留了原始格式。

答案 2 :(得分:-1)

此解决方案可以满足您的要求,此外它还适用于每行的值可能包含空格的情况。

use strict;
use warnings;

my %data;

for my $file (qw/ dog2.txt dog1.txt /) {

  open my $fh, '<', $file or die $!;

  while (<$fh>) {
    $data{$1} = $2 if /(\S+)\s+(\S(?:.*\S)?)/;
  }
}

while (my ($key, $val) = each %data) {
  print "$key $val\n";
}

<强>输出

poodle 8888
spaniel 9843
germanshepard 3267
beagle 6784
jackrussel 5743