我正在使用以下命令比较两个大型逗号分隔 CSV 文件 File1.csv
和 File2.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/