【问题标题】:Perl: Compare Two CSV Files and Print out matches (modifying this code)Perl:比较两个 CSV 文件并打印出匹配项(修改此代码)
【发布时间】:2014-12-08 13:54:29
【问题描述】:

我是 perl 的新手,并在以下位置发现了解决方案: Perl: Compare Two CSV Files and Print out differences

我已经经历了几十个其他解决方案,这是最接近的,除了我不想找到 2 个 CSV 文件之间的差异,我想找到第二个 CSV 文件在列和行中与第一个文件匹配的位置。我如何修改以下脚本以查找列/行中的匹配项而不是差异。我希望剖析这段代码并从那里学习数组,但想找出这个应用程序的解决方案。非常感谢。

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";

好的,我意识到这正是我想要的:

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;

现在我想搜索 file2 的散列以检查 file1 的散列中是否有任何匹配项。这就是我卡住的地方

有了新的代码建议,代码现在看起来像这样

#!/usr/bin/env perl
use strict;
use warnings;
use feature qw(say);
use autodie;

use constant {
    FILE_1  => "masterlist.csv",
    FILE_2  => "pastebin.csv",
};

#
# 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;

    my %hash2;
open my $file2_fh, "<", FILE_2;
while ( my $value = <$file2_fh> ) {
    chomp $value;
    if ( $hash1{$value} ) { 
       print "Match found $value\n";
       $hash2{$value}++;
    }
}
close $file2_fh;

print "Matches found:\n";
foreach my $key ( keys %hash2 ) {
    print "$key found $hash2{$key} times\n";
}

我用 split() 更新了一个部分,它似乎可以工作,但必须进行更多测试以确认它是否适合我正在寻找的解决方案,或者我还有更多工作要做

#
# 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} = ( %hash1, (split(/,/, $_))[1,2] );
}
close $file1_fh;

【问题讨论】:

  • “匹配”是什么意思?您是指两个不同文件中独立但相同的行吗?您的意思是逐行比较两者,并且当且仅当该特定行在两个文件中包含相同时才“匹配”?我不确定我是否会将您发布的代码称为一个良好的起点 - 它可能会很好地工作,但会遇到通常的 perl 问题,即有点难以理解。
  • 例如,我想将第一个 CSV 中的一个列和行中的单个值与第二个 CSV 中的任何列和行中的值进行匹配。我找到了匹配整行的其他解决方案,但我只想匹配单个单元格。第一个 CSV 较小,第二个 CSV 较大。
  • 你需要做的是一个哈希。读取您的 CSV 文件,将要查找的字段存储在哈希中。然后遍历其他 CSV 文件并打印与哈希条目匹配的行。
  • 好的,我想我明白了。非常感谢:)
  • 我已经用更相关的代码更新了这个问题

标签: perl csv


【解决方案1】:

所以,有了你的代码——你已经在“file1”中读入了一个哈希值。

为什么不将文件 2 读入哈希,而是这样做:

my %hash2;
open my $file2_fh, "<", FILE_2;
while ( my $value = <$file2_fh> ) {
    chomp $value;
    if ( $hash1{$value} ) { 
       print "Match found $value\n";
       $hash2{$value}++;
    }
}
close $file2_fh;

print "Matches found:\n";
foreach my $key ( keys %hash2 ) {
    print "$key found $hash2{$key} times\n";
}

【讨论】:

  • 谢谢!这似乎好多了,但我收到: ./printmatches.pl 第 27 行的 ./printmatches.pl 语法错误,“if $hash1”附近 全局符号“$value”需要 ./printmatches.pl 第 28 行的显式包名. 全局符号“%hash2”在 ./printmatches.pl 第 29 行需要显式包名。全局符号“$value”在 ./printmatches.pl 第 29 行需要显式包名。语法错误在 ./printmatches.pl 第 31 行, “}”附近 全局符号“%hash2”需要在 ./printmatches.pl 第 35 行显示包名。全局符号“%hash2”需要在 ./printmatches.pl 第 36 行显示包名。
  • 27 是:如果 $hash1{$value} {
  • 代码 sn-p 已编辑。需要声明%hash2(并在'if'中加上括号)。
  • 太棒了!谢谢!它似乎与线条相匹配。我想知道如何让它匹配单元格,或者我是否可以通过这种方式做到这一点。如果我有一个 2 列的 CSV 作为文件一,一个 3 列的 CSV 作为文件二,或者一个 1 列的 csv 作为文件一,一个 2 列的 csv 作为文件 2,它会告诉我不匹配。另外,如果我切换文件 1 中的列(假设两个 csv 都是 2 列),而不是文件 2,它会告诉我没有匹配项。
  • 快速简便的方法:使用split()。但这并不是 100% 可靠的,因为 CSV 确实允许您在引号内的字段中嵌入分隔符。如果您确定您的数据中不会发生这种情况,split 就可以解决问题。
【解决方案2】:

我认为这段代码标识了文件 A 中的数据字段与文件 B 中的数据字段匹配的每个地方(至少它在我有限的测试数据上是这样):

use strict;
use warnings;
my @arr1;
my @arr2;

# a.txt -> @arr1

my $file_a_name = "poster_a.txt";
open(FIL,$file_a_name) or die("$!");
my $a_line_counter = 0;
while (my $a_line = <FIL>)
{
    $a_line_counter = $a_line_counter + 1;
    chomp($a_line); 
    my @fields = (split /,/,$a_line);
    my $num_fields = scalar(@fields);
    s{^\s+|\s+$}{}g foreach @fields;
    push @arr1, \@fields if ( $num_fields ne 0);
};;

close(FIL);
my $file_b_name = "poster_b.txt";
open(FIL,$file_b_name) or die("$!");

while (my $b_line = <FIL>)
{
    chomp($b_line); 
    my @fields = (split /,/,$b_line);    
    my $num_fields = scalar(@fields);
    s{^\s+|\s+$}{}g foreach @fields;
    push @arr2, \@fields if ( $num_fields ne 0) 
};
close(FIL);

# b.txt -> @arr2

#print "\n",@arr2, "\n";


my @match_array;
my $file_a_line_ctr = 1;
foreach my $file_a_line_fields (@arr1) 
{
    my $file_a_column_ctr = 1;
    foreach my $file_a_line_field (@{$file_a_line_fields})
    {
        my $file_b_line_ctr = 1;
        foreach my $file_b_line_fields(@arr2)
        {
            my $file_b_column_ctr = 1;
            foreach my $file_b_field (@{$file_b_line_fields})
            {
                if ( $file_b_field eq $file_a_line_field ) 
                {
                    my $match_info = 
                      "$file_a_name line $file_a_line_ctr column $file_a_column_ctr"  .
                      "  (${file_a_line_field}) matches: "  .
                      "$file_b_name line $file_b_line_ctr column $file_b_column_ctr ";
                    push(@match_array, $match_info);
                    print "$match_info \n";
                }
                $file_b_column_ctr = $file_b_column_ctr + 1;
            }
            $file_b_line_ctr = $file_b_line_ctr + 1;               
        }
        $file_a_column_ctr = $file_a_column_ctr + 1;
    }
    $file_a_line_ctr = $file_a_line_ctr + 1;
}
print "there were ", scalar(@match_array)," matches\n";

【讨论】:

  • 酷!这也有效,但它似乎只匹配完整的行。如果以相同的顺序存在相同的列,则匹配。在第二个文件中添加一个额外的列,什么都没有。这也是csv,我不确定是不是用文本文件测试过
  • @perlstudent 它不必匹配我的测试数据中的整行。如果我在第一个文件中有一行 1,2,3,4,5,在第二个文件中有 1,2,3,4,5,6,我会得到 5 个匹配项。如果我有 a,b,c,d,e,f 在第一个和 a,b,c,d,e 在第二个。你能提供你的测试数据吗?
  • 确定我的第一个文件是:uname;e-mail john;john@company.com jane;jane@company.com
  • 第二个文件是:uname;e-mail;column3 john;john@company.com;column3 jo;jo@company.com;column3 bo;bo@company.com;column3
  • 您使用 分号 作为分隔符,而不是 逗号。所以split语句需要修改为使用分号。一旦完成,它将匹配 John 字段和 john@company.com 字段以及标题字段 uname 和 email。可以通过在初始读取循环中不推送每个文件的第 1 行数据来删除标头字段。这也需要最终循环 line_ctr 从 2 开始以提供准确的信息。
猜你喜欢
  • 1970-01-01
  • 2013-06-17
  • 2012-09-05
  • 2014-02-21
  • 1970-01-01
  • 2017-07-30
  • 2016-12-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多