perl - 使用 Perl 或 Powershell,如何比较 2 个 CSV 文件并仅获取新行?

标签 perl powershell csv compare diff

我正在使用以下命令比较两个大型逗号分隔 CSV 文件 File1.csvFile2.csv Text::Diff Perl 模块。 Perl 程序是从 .bat 文件调用的,我将结果放入第三个文件 Diff.csv

Perl

#!/usr/bin/env perl

use strict;
use warnings;

use Text::Diff;

my $diffs = diff $ARGV[0] => $ARGV[1];

$diffs =~ s/^(?:[^\n]*+\n){2}//;
$diffs =~ s/^(?:[\@ ][^\n]*+)?+\n//mg;

print $diffs;

这就是我调用 Perl 脚本的方式:

perl "C:\diffBetweenTwoFiles.pl" "C:\File1.csv" "C:\File2.csv" > "C:\Diff.csv"

CSV 文件中的一列是名称

当前结果列出了任何列中的值发生更改的所有行,但我只想列出新的 Name 行。

例如:

文件1.csv

"Name","DOB","Address"
"One","1/1/01","5 Stock Rd"
"Two","1/2/02","1 Research Rd"

文件2.csv

"Name","DOB","Address"
"One","1/1/01","5 Stock Rd"
"Two","1/2/02","111 Research Rd"
"Three","1/3/03","3 Bold Rd"

目前,结果列出了这些(其中包括“两个”,因为其地址已更改):

"Name","DOB","Address"
"Two","1/2/02","111 Research Rd"
"Three","1/3/03","3 Bold Rd"

但是,我只想结果列出新的“名称”,如下所示:

"Name","DOB","Address"
"Three","1/3/03","3 Bold Rd"

如何在 Perl 或 Powershell 脚本中执行此操作?

最佳答案

使用Text::CSV在 Perl 中

use warnings;
use strict;
use feature 'say';
use Text::CSV;

my ($file_old, $file_new, $file_diff) = 
    map { $_ . '.csv' } qw(File1 File2 Diff);

my $csv = Text::CSV->new ( { binary => 1 } )
    or die "Cannot use CSV: ".Text::CSV->error_diag();

my ($old, $header) = get_lines($csv, $file_old, 1);
my $new            = get_lines($csv, $file_new);

my @lines_with_new_names = @{ new_names($old, $new) };

open my $fh, '>', $file_diff  or die "Can't open $file_diff: $!";
$csv->say($fh, $header);
$csv->say($fh, $_) for @lines_with_new_names;  # or print with eol set

sub new_names {
    my ($old, $new) = @_;
    my %old = map { $_->[0] => 1 } @$old;
    return [ map { (!exists $old{$_->[0]}) ? $_ : () } @$new ];
}

sub get_lines {
    my ($csv, $file, $return_header) = @_;
    open my $fh, '<', $file or die "Can't open $file $!";
    my $header = $csv->getline($fh);  # remove the header line
    return ($return_header) 
        ? ( $csv->getline_all($fh), $header )
        :   $csv->getline_all($fh);
}

这将打印与提供的示例的正确差异。

标有old的变量名与行数较少的文件相关,另一个是new。 “名称”列被视为第一个列。

评论

  • getline_all 方法返回所有行的 arrayref,其中每行都是包含所有字段的 arrayref。这是通过子程序完成的,还可以选择返回标题行。

  • 此处另一个变量的可选返回会影响返回单个标量还是列表,因此也可以使用 wantarray 来处理。内置

    return wantarray ? ( LIST ) : scalar;
    

    如果在列表上下文中调用子函数,则返回 true。因此,调用者决定在列表或标量上下文中调用 sub,my ($v1, $v2) = f(...)my $v = f(.. .),在这种情况下,调用中不需要标志。我选择了更明确的方式。

  • 名称列表中的差异是在 new_names 子文件中生成的。首先,使用“旧”数组引用中的所有名称进行查找哈希。然后,过滤"new"arrayref 中的行,获取“旧”中没有名称的行(散列中没有这样的键),并在 arrayref [] 中返回。

    这种哈希的使用是查找数组之间差异的标准技术。

记录的用于打印的方法say在我测试该模块的旧版本上不起作用。在这种情况下,请使用 print 并设置 eol在构造函数中。

关于perl - 使用 Perl 或 Powershell,如何比较 2 个 CSV 文件并仅获取新行?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50590776/

相关文章:

PowerShell:引用带有空格的属性

PowerShell:如何grep命令输出?

csv - 如何禁用流利的csv格式程序中包含定界符的字段值周围的引号?

linux - Bash:解析 CSV 并编辑单元格值

perl - jsvc (tomcat) 在使用反引号运行然后失效时无法正确守护进程

regex - 在 perl 中打印模式

c - Google.com 是如何创建的?

Perl Authen::OATH 和 Google Authenticator - 不兼容?

powershell - 使用PowerShell创建类库并将其添加到解决方案

powershell - 使用PowerShell从CSV文件中删除重复项