如何让批处理调用的Perl脚本比较大CSV文件仅输出差异结果?
解决CSV比较结果仅保留差异内容的问题
咱们先捋清楚核心问题:你的结果文件里混了表头和无关内容,大概率是Perl脚本没跳过表头,或者把两个文件里的相同行也输出了。下面给你两种解决方案,优先推荐修改Perl脚本的方式——毕竟处理大型CSV,脚本层面的精准控制效率更高、更可靠。
方案一:修改Perl脚本(推荐,适配大文件)
假设你之前用的是Perl标准的Text::CSV模块处理CSV(这能避免单元格里的逗号、引号搞乱对比逻辑),我给你写两个版本的脚本:一个是单向差异(仅输出B有但A没有的行),另一个是双向差异(输出A有B没有、B有A没有的所有差异行)。
首先确保你已经安装了Text::CSV模块,没装的话用cpan Text::CSV或cpanm Text::CSV安装即可。
单向差异脚本(仅保留B独有的行)
use strict; use warnings; use Text::CSV; # 配置CSV解析规则,根据你的CSV格式调整(比如分隔符、引号类型) my $csv = Text::CSV->new({ binary => 1, auto_diag => 1, sep_char => ',', quote_char => '"' }) or die "CSV初始化失败: " . Text::CSV->error_diag(); # 读取第一个CSV,跳过表头,把所有行存进哈希(用|拼接行内容当键,避免逗号干扰) my %file1_rows; open my $fh1, '<', $ARGV[0] or die "无法打开文件 $ARGV[0]: $!"; $csv->getline($fh1); # 跳过表头 while (my $row = $csv->getline($fh1)) { $file1_rows{join('|', @$row)} = 1; } close $fh1; # 读取第二个CSV,跳过表头,对比每行是否在第一个文件中,不在则写入结果 open my $fh2, '<', $ARGV[1] or die "无法打开文件 $ARGV[1]: $!"; $csv->getline($fh2); # 跳过表头 open my $out_fh, '>', $ARGV[2] or die "无法创建输出文件 $ARGV[2]: $!"; while (my $row = $csv->getline($fh2)) { my $row_str = join('|', @$row); unless ($file1_rows{$row_str}) { $csv->print($out_fh, $row); print $out_fh "\n"; # 确保每行末尾换行 } } close $fh2; close $out_fh;
双向差异脚本(保留所有独有的行)
如果需要同时找出两个文件各自独有的行,用这个版本:
use strict; use warnings; use Text::CSV; my $csv = Text::CSV->new({ binary => 1, auto_diag => 1, sep_char => ',' }); my (%file1_rows, %file2_rows); # 读取文件1,跳过表头,存储所有行 open my $fh1, '<', $ARGV[0] or die $!; $csv->getline($fh1); # 跳过表头 while (my $row = $csv->getline($fh1)) { $file1_rows{join('|', @$row)} = $row; } close $fh1; # 读取文件2,跳过表头,存储所有行 open my $fh2, '<', $ARGV[1] or die $!; $csv->getline($fh2); # 跳过表头 while (my $row = $csv->getline($fh2)) { $file2_rows{join('|', @$row)} = $row; } close $fh2; # 写入差异结果 open my $out_fh, '>', $ARGV[2] or die $!; # 如果需要在结果里加表头,取消下面两行注释 # my @headers = @{$csv->getline($fh1)}; # 重新打开文件读表头,或者直接写死表头 # $csv->print($out_fh, \@headers); print $out_fh "\n"; # 输出文件1独有的行 for my $row_str (keys %file1_rows) { unless ($file2_rows{$row_str}) { $csv->print($out_fh, $file1_rows{$row_str}); print $out_fh "\n"; } } # 输出文件2独有的行 for my $row_str (keys %file2_rows) { unless ($file1_rows{$row_str}) { $csv->print($out_fh, $file2_rows{$row_str}); print $out_fh "\n"; } } close $out_fh;
脚本关键说明:
- 明确跳过了两个输入文件的表头,所以结果文件默认不会包含表头(如果需要表头,取消对应注释即可)
- 用哈希存储行内容,对比效率极高,适合GB级别的大型CSV
- 用
|拼接行内容当哈希键,避免CSV单元格内的逗号、引号破坏对比逻辑
方案二:批处理临时过滤结果(适合小文件应急)
如果不想改Perl脚本,也可以在批处理里加一步过滤,去掉表头和重复行。假设你的临时结果文件叫temp_result.csv,可以这么写:
@echo off rem 调用原Perl脚本生成临时结果 perl your_old_script.pl file1.csv file2.csv temp_result.csv rem 跳过第一行(表头),然后过滤重复行(依赖git bash或Unix工具的sort/uniq) more +1 temp_result.csv > temp1.csv sort temp1.csv | uniq -u > final_diff.csv rem 删除临时文件 del temp_result.csv temp1.csv echo 处理完成,结果已保存到final_diff.csv pause
⚠️ 注意:这个方法只适合小型CSV,大文件用sort会很慢,而且如果CSV行内有换行(非标准情况)会出错,所以优先用方案一。
批处理调用修改后的脚本
把你的批处理改成这样,就能直接传参调用新脚本:
@echo off set "PERL_SCRIPT=compare_diff.pl" set "INPUT_CSV1=your_first_file.csv" set "INPUT_CSV2=your_second_file.csv" set "OUTPUT_DIFF=final_diff.csv" perl "%PERL_SCRIPT%" "%INPUT_CSV1%" "%INPUT_CSV2%" "%OUTPUT_DIFF%" echo 差异比较完成,结果文件:%OUTPUT_DIFF% pause
内容的提问来源于stack exchange,提问作者faujong
相关产品推荐
相关产品推荐

