使用Perl比较两个CSV文件

时间:2014-04-28 19:41:13

标签: arrays perl csv hash compare

我有两个要与Perl进行比较的CSV文件。

我有使用Text::CSV::Slurp将文件导入Perl的代码,它为我提供了一个很好的文件散列引用数组。

使用Data::Dumper::Concise可以正确显示我的所有数据导入。

use strict;
use warnings;

use Text::CSV::Slurp;
use Data::Dumper::Concise;

my $file1_src = "IPB-CSV.csv";

my $file2_src = "SRM-CSV.csv";

my $IPB = Text::CSV::Slurp->load(file => $file1_src);
my $SRM = Text::CSV::Slurp->load(file => $file2_src);

print Dumper($IPB);
print Dumper($SRM);

转储的结果看起来像这样

$ IPB

[
  {
    Drawing => "1001"
  },
  {
    Drawing => "1002"
  },
  {
    Drawing => "1003"
  }
]

$ SRM

[
  {
    Drawing => "1001",
    Figure => "Figure 2-8",
    Index => 2,
    Nomenclature => "Some Part"
  },
  {
    Drawing => "1002",
    Figure => "Figure 2-8",
    Index => 2,
    Nomenclature => "Some Part"
  },
  {
    Drawing => "2001",
    Figure => "Figure 2-8",
    Index => 2,
    Nomenclature => "Some Part"
  },
  {
    Drawing => "2002",
    Figure => "Figure 2-8",
    Index => 2,
    Nomenclature => "Some Part"
  }
]

我想根据每个哈希的Drawing键比较两个数组,并创建两个CSV文件,如下所示

  • 包含$IPB但不包含$SRM的项目,其中仅包含“绘图列”中的数据。

  • 该项目位于$SRM但不包含$IPB的其他位置,其中包含与Drawing列相关的所有字段。

我找到了很多信息来比较文件以查看它们是否匹配,或者比较单个数据片段的哈希值或数组,但我无法找到特定于我需要的内容。

3 个答案:

答案 0 :(得分:1)

由于绘图是一种排序标准,为什么不将数据“索引”到一个更方便的地方,其中绘图索引是关键,相应的数据是相应的值?

my %ipb;
for my $record ( @$IPB ) {
    my $index = $record->{Drawing};
    push @{ $ipb{$index} }, $record;
}

my %srm;
for my $record ( @$SRM ) {
    my $index = $record->{Drawing};
    push @{ $srm{$index} }, $record;
}

现在应该轻而易举地找出$IPB$SRM的唯一索引:

use List::MoreUtils 'uniq';
my @unique_ipb = uniq( grep { $ipb{$_} and not $srm{$_} } keys( %ipb ), keys( %srm ) );
my @unique_srm = uniq( grep { $srm{$_} and not $ipb{$_} } keys( %ipb ), keys( %srm ) );

两者有什么共同之处?

my @intersect = uniq( grep { $srm{$_} and $ipb{$_} } keys( %ipb ), keys( %srm ) );

绘制索引1002的所有图号是什么?

print $_->{Figure}, "\n" for @{ $ipb{1002} // [] }, @{ $srm{1002} // [] };

答案 1 :(得分:1)

这个简短的程序使用$ipb$srm的示例值,并创建我认为您想要的输出。 (除了包名等全局标识符外,不要使用大写字母。)

有几个问题

  • 使用Text::CSV::Slurp会为您留下两个哈希数组,这些哈希数对此任务没有用,无需进一步索引。通过逐行处理文件,从头开始创建适当的数据结构会更好。

  • 您说您的第二个文件必须包含与每个Drawing密钥相关的所有信息,但是,由于Perl哈希本质上是无序的,Text::CSV::Slurp已丢失字段名称的顺序。可以做的最好的事情是按照找到的顺序打印数据,但是在显示字段名称的标题行之前。这是避免Text::CSV::Slurp

  • 的另一个原因

use strict;
use warnings;
use autodie;

# The original data

my $ipb = [{ Drawing => 1001 }, { Drawing => 1002 }, { Drawing => 1003 }];

my $srm = [
  {
    Drawing => "1001",
    Figure => "Figure 2-8",
    Index => 2,
    Nomenclature => "Some Part"
  },
  {
    Drawing => "1002",
    Figure => "Figure 2-8",
    Index => 2,
    Nomenclature => "Some Part"
  },
  {
    Drawing => "2001",
    Figure => "Figure 2-8",
    Index => 2,
    Nomenclature => "Some Part"
  },
  {
    Drawing => "2002",
    Figure => "Figure 2-8",
    Index => 2,
    Nomenclature => "Some Part"
  }
];

# Index the data

my %srm;
for my $item (@$srm) {
  my $drawing = $item->{Drawing};
  $srm{$drawing} = $item;
}

my %ipb;
for my $item (@$ipb) {
  my $drawing = $item->{Drawing};
  $ipb{$drawing} = 1;
}

# Create the output files

open my $csv1, '>', 'file1.csv';
for my $id (sort keys %ipb) {
  next if $srm{$id};
  print $csv1 $id, "\n";
}
close $csv1;

open my $csv2, '>', 'file2.csv';
my @keys = keys %{ $srm->[0] };
print $csv2 join(',', @keys), "\n";
for my $id (sort keys %srm) {
  next if $ipb{$id};
  print $csv2 join(',', @{$srm{$id}}{@keys}), "\n"; 
}
close $csv2;

<强>输出

<强> file1.csv

1003

<强> file2.csv

Drawing,Nomenclature,Index,Figure
2001,Some Part,2,Figure 2-8
2002,Some Part,2,Figure 2-8

答案 2 :(得分:0)

这有点复杂,因为您的数据结构不太适合比较。您有对散列引用数组的引用,并且您关心hashref的其中一个键中的数据。我的第一步是将IPB压平为一个数组(因为此下没有数据),并将SRM转换为单个hashref。

my @ipbarray = map { ${$_}{Drawing} } $IPB; # Creates an array from IPB.
my $srmhash = {};
for my $hash ($SRM) {
  ${$srmhash}{${$hash}{Drawing}} = $hash unless defined ${$srmhash}{${$hash}{Drawing}}; # Don't overwrite if it exists
}

现在我们还有两个可行的数据结构。

下一步是对比这些值:

my @ipbonly = ();
my @srmonly = ();

for my $ipbitem (@ipbarray) {
  push @ipbonly, ( Drawing => $ipbitem } unless defined ${$srmhash}{$ipbtem};
}

for my $srmitem (keys $srmhash) {
  push @srmonly, ${$srmhash}{$srmitem} unless grep { $_ == $srmitem } @ipbarray;
}

此时,@ ipbonly和@srmonly将包含您想要的数据。