Perl:比较两个CSV文件并打印出差异

时间:2013-06-20 16:16:26

标签: perl csv compare

我是Perl的菜鸟,我很难完成这项任务。我有两个单列的CSV文件,我正在尝试将差异打印到第三个文件。

File1:
123
124
125
126

File2:
123
124
127

Expected Output:
125
126
127

这是我到目前为止所做的,但是没有用:

#!/usr/bin/perl

use strict;
use warnings;

my $sheet_1;
my $sheet_2;
my $count1 = 0;
my $count2 = 0;

my $file1 = 'file1.csv';
my $file2 = 'file2.csv';
my $file_out = 'output.csv';

open (FILE1, "<$file1")  or die "Couldn't open input file: $!"; 
open (FILE2, "<$file2")  or die "Couldn't open input file: $!"; 


while( <FILE1> ) {
  chomp;
  $count1++;
  #skip header;
  next unless $count1;
  my $row_1;
  @$row_1 = split( /,/, $_ );
  push @$sheet_1, $row_1;
}
@$sheet_1 = sort { $a->[0] <=> $b->[0] } @$sheet_1;

while( <FILE2> ) {
  chomp;
  $count2++;
  #skip header;
  next unless $count2;
  my $row_2;
  @$row_2 = split( /,/, $_ );
  push @$sheet_2, $row_2;
}

@$sheet_2 = sort { $a->[0] <=> $b->[0] } @$sheet_2;


OUTER: {
     foreach my $row_1 ( @$sheet_1 ) {
         foreach my $row_2 ( @$sheet_2 ) {
        if (@$row_1[0] eq @$row_2[0]){
        last OUTER
        }
        else{
        print "@$row_1[0]\n";
        }
        }
    }
}

close FILE1;
close FILE2;

3 个答案:

答案 0 :(得分:1)

您可以使用Text::Diff Perl模块执行此操作。否则,请参阅以下内容:

这是进行比较的一种算法。

use strict;
my @arr1;
my @arr2;
my $a;

open(FIL,"a.txt") or die("$!");
while (<FIL>)
    {chomp; $a=$_; $a =~ s/[\t;, ]*//g; push @arr1, $a if ($a ne  '');};
close(FIL);

open(FIL,"b.txt") or die("$!");
while (<FIL>)
    {chomp; $a=$_; $a =~ s/[\t;, ]*//g; push @arr2, $a if ($a ne  '');};
close(FIL);

my %arr1hash;
my %arr2hash;
my @diffarr;
foreach(@arr1) {$arr1hash{$_} = 1; }
foreach(@arr2) {$arr2hash{$_} = 1; }

foreach $a(@arr1)
{
    if (not defined($arr2hash{$a})) 
     {
        push @diffarr, $a;
     }
}

foreach $a(@arr2)
{
   if (not defined($arr1hash{$a})) 
   { 
       push @diffarr, $a;
   }
}

print "Diff:\n";
foreach $a(@diffarr)
{
    print "$a\n";
}
# You can print to a file instead, by: print FIL "$a\n";

答案 1 :(得分:1)

查看diffcomm。这些可能会做你想要的。

现在提出几个问题:

  1. 如果这些文件每行只有一个值,那么是什么使它们成为CSV文件? CSV文件有多个以逗号分隔的列(CSV =逗号分隔值)。是否还有其他事情发生。
  2. 如果两个文件具有相同的值,但在两个不同的位置,您是否将其视为差异?想象一个包含三行的文件,这些行包含1, 2, 3。您将其与其中包含1, 3, 2的第二个文件进行比较。第二行和第三行有所不同吗?或者,文件是否相同,因为它们包含相同的值?

  3.   

    不,如果两个文件在不同的地方都有相同的值,则该值不应该在输出中。在您的示例中,两个文件(1,2,3)和(1,3,2)是相同的。 - Yoboy 7小时前

    很好......

    每当是第2组类型的第1组中的项目时,您应该考虑哈希。

    散列是一个值列表,其中每个值都有一个。可以是列表中的重复值,但只能是特定键的单个实例。这意味着您可以轻松查看列表中是否已存在密钥。

    想象一下,获取文件#1,并将每个值作为密钥放在哈希中。价值观无关紧要,你只关心钥匙。

    现在,当您浏览文件#2时,您可以快速查看该密钥是否已存在于您的哈希中。如果是,则为重复值。

    我们还可以利用哈希的第二个特性:只允许一个密钥实例。如果我们将两只苍蝇扔进一个哈希怎么办?如果文件#1和文件#2之间的值重复,则无关紧要,该密钥只​​能有一个实例。

    以下是获取两个文件中唯一值列表的方法:

    use strict;
    use warnings;
    use feature qw(say);
    use autodie;
    
    use constant {
        FILE_1  => "file1.txt",
        FILE_2  => "file2.txt",
    };
    
    my %hash;
    #
    # Load the Hash with value from File #1
    #
    open my $file1_fh, "<", FILE_1;
    while ( my $value = <$file1_fh> ) {
        chomp $value;
        $hash{$value} = 1;
    }
    close $file1_fh;
    #
    # Add File #2 to the Hash
    #
    open my $file2_fh, "<", FILE_2;
    while ( my $value = <$file2_fh> ) {
        chomp $value;
        $hash{$value} = 1;   #If that value was in "File #1", it will be "replaced"
    }
    close $file2_fh;
    
    #
    # Now print out everything
    #
    for my $value ( sort keys %hash ) {
        say $value;
    }
    

    这将打印出来:

    123
    124
    125
    126
    127
    

    您想要的是唯一值的列表。这比起初看起来有点棘手。您可以将文件#1的值放入哈希值,然后如果它们不在文件#1中,则打印出文件#2中的值。这将为您提供文件#2中的唯一值列表,但不是文件#1中的唯一值。

    因此,您需要创建两个哈希值,一个用于FIle#1,另一个用于文件#2,然后通过每个哈希并将它们相互比较:

    use strict;
    use warnings;
    use feature qw(say);
    use autodie;
    
    use constant {
        FILE_1  => "file1.txt",
        FILE_2  => "file2.txt",
    };
    
    #
    # Load Hash #1 with value from File #1
    #
    my %hash1;
    open my $file1_fh, "<", FILE_1;
    while ( my $value = <$file1_fh> ) {
        chomp $value;
        $hash1{$value} = 1;
    }
    close $file1_fh;
    
    #
    # Load Hash #2 with value from File #2
    #
    my %hash2;
    open my $file2_fh, "<", FILE_2;
    while ( my $value = <$file2_fh> ) {
        chomp $value;
        $hash2{$value} = 1;
    }
    close $file2_fh;
    

    现在,我们需要将一个与另一个进行比较。我现在将值存储在数组中:

    my @array;
    #
    # Check if File #1 has unique values vs File #2
    #
    for my $value ( %keys %hash1 ) {
       if ( not exists $hash2{$value} ) {
          push @array, $value;  #Value in File #1, but not in File #2
       }
    }
    #
    # Check if File #2 has unique values vs File #1
    #
    for my $value ( %keys %hash2 ) {
       if ( not exists $hash1{$value} ) {
          push @array, $value;  #Value in File #2, but not in File #1
       }
    }
    #
    # Now print out what's in @array of unique values
    #
    for my $value ( sort @array ) {
        say $value;
    }
    

答案 2 :(得分:0)

  1. 如果是单列,则没有可用的逗号分隔。你为什么这样做?只需将文件拆分为“\ n”
  2. 即可
  3. 不要重新发明轮子。如果它是具有多列的实际CSV,请使用Text :: CSV :: Slurp之类的内容来阅读它
  4. 您不是在查找项目时循环遍历每个文件的全部,而是使用哈希作为查找。但是,如果您正在处理大型文件,则可能会遇到内存问题。
  5. 即:

    use strict;
    use warnings;
    use 5.012;
    
    use Text::CSV::Slurp;
    
    my $file1_src=<<EOF;
    id,field1,field2,field3
    123,junk,"quoted junk",junk 
    124,"quoted junk","quoted junk",junk 
    125,junk,"quoted junk",junk 
    126,junk,"quoted junk",junk 
    EOF
    
    my $file2_src=<<EOF;
    id,field1,field2,field3
    123,junk,"quoted junk",junk 
    124,junk,"quoted junk",junk 
    127,"quoted junk","quoted junk",junk
    EOF
    
    my %data1 = map { $_->{id} => 1 } @{Text::CSV::Slurp->load(string => $file1_src)};
    my %data2 = map { $_->{id} => 1 } @{Text::CSV::Slurp->load(string => $file2_src)};
    
    for my $id (keys %data1, keys %data2) {
      say $id unless $data1{$id} and $data2{$id};
    }