从CSV文件中删除/提取基于唯一/重复ID的行

时间:2015-10-13 10:24:25

标签: perl csv

根据您的查看方式,我需要根据Id是唯一的来删除行,如果Id有重复项,则需要提取行(保留所有重复项)。 我不确定/没有足够的Perl知识来实现​​这一目标。我找到了类似的主题,但没有取得多大成功。这些是我使用example 1example 2example 3的示例。在之前的问题中,有人向我展示了一个使用List :: MoreUtils模块的解决方案,因此我可以将值与公共ID合并。现在情况并非如此,如果id是唯一的,则会删除行。我知道我可以使用List :: MoreUtils模块执行此操作,但我想在没有它的情况下执行此操作。这是我的虚拟数据(复制了其他问题的示例数据,因为数据无关紧要),在这里你可以看到我在追求的东西。订单并不重要。

之前:

Cat_id;Cat_name;Id;Name;Amount;Colour;Bla
101;Fruits;50010;Grape;500;Red;1
101;Fruits;50020;Strawberry;500;Red;1
201;Vegetables;60010;Carrot;500;White;1
101;Fruits;50060;Apple;1000;Red;1
101;Fruits;50030;Banana;1000;Green;1
101;Fruits;50060;Apple;500;Green;1
101;Fruits;50020;Strawberry;1000;Red;1
201;Vegetables;60010;Carrot;100;Purple;1
101;Fruits;50020;Strawberry;200;Red;1

后:

Cat_id;Cat_name;Id;Name;Amount;Colour;Bla
101;Fruits;50020;Strawberry;500;Red;1
201;Vegetables;60010;Carrot;500;White;1
101;Fruits;50060;Apple;1000;Red;1
101;Fruits;50060;Apple;500;Green;1
101;Fruits;50020;Strawberry;1000;Red;1
201;Vegetables;60010;Carrot;100;Purple;1
101;Fruits;50020;Strawberry;200;Red;1

您可以看到已删除ID为50010和50030的Grape和Banana行,因为两者都只有一个条目。

这是我的脚本,我正在讨论从哈希中选择唯一值并输出它们的部分(考虑到Text :: CSV_XS模块)。有人能告诉我怎么做吗?

#!/usr/bin/perl -w
use strict;
use warnings;
use Text::CSV_XS;

my $inputfile = shift || die "Give input and output names!\n";
my $outputfile = shift || die "Give output name!\n";

open (my $infile, '<:encoding(iso-8859-1)', $inputfile) or die "Sourcefile in use / not found :$!\n";
open (my $outfile, '>:encoding(UTF-8)', $outputfile) or die "Outputfile in use :$!\n";

my $csv_in = Text::CSV_XS->new({binary => 1,sep_char => ";",auto_diag => 1,always_quote => 1,eol => $/}); 
my $csv_out = Text::CSV_XS->new({binary => 1,sep_char => "|",auto_diag => 1,always_quote => 1,eol => $/});

my $header = $csv_in->getline($infile);
$csv_out->print($outfile, $header);

my %data;

while (my $elements = $csv_in->getline($infile)){
    my @columns = @{ $elements };       
    my $id = $columns[2];
    push @{ $data{$id} }, \@columns;
}

for my $id ( sort keys %data ){                 # Sort not important
    if @{ $data{$id} } > 1                      # Here I have no idea anymore..
        $csv_out->print($outfile, \@columns);   #
}

1 个答案:

答案 0 :(得分:0)

我认为我不会在整个数据集中加载哈希,而是继续读取文件两次,加载只有ID值的哈希值。这肯定会花费更长的时间,但随着文件的增长,将所有数据都存储在内存中可能会有缺点。

那就是说,我没有使用Text::CSV_XS,但这是我想到的一个名义的想法。

my %count;

open (my $infile, '<:encoding(iso-8859-1)', $inputfile) or die;
open (my $outfile, '>:encoding(UTF-8)', $outputfile) or die;

while (<$infile>) {
  next if $. == 1;
  my ($id) = (split /;/, $_, 4)[2];
  $count{$id}++;
}

seek $infile, 0, 0;

while (<$infile>) {
  my @fields = split /;/;
  print $outfile join '|', @fields if $count{$fields[2]} > 1 or $. == 1;    
}

close $infile;
close $outfile;

最后$. == 1是您不会丢失标题行。

- 编辑 -

#!/usr/bin/perl -w

use strict;
use warnings;
use Text::CSV_XS;

my $inputfile = shift || die "Give input and output names!\n";
my $outputfile = shift || die "Give output name!\n";

open (my $infile, '<:encoding(iso-8859-1)', $inputfile) or die;
open (my $outfile, '>:encoding(UTF-8)', $outputfile) or die;

my $csv_in = Text::CSV_XS->new({binary => 1,sep_char => ";",
    auto_diag => 1,always_quote => 1,eol => $/}); 
my $csv_out = Text::CSV_XS->new({binary => 1,sep_char => "|",
    auto_diag => 1,always_quote => 1,eol => $/});

my ($count, %count) = (1);

while (my $elements = $csv_in->getline($infile)){
  $count{$$elements[2]}++;
}

seek $infile, 0, 0;

while (my $elements = $csv_in->getline($infile)){
  $csv_out->print($outfile, $elements)
    if $count{$$elements[2]} > 1 or $count++ == 1;
}

close $infile;
close $outfile;