合并不同长度的多个CSV文件及Perl脚本适配方案咨询
适配不同长度CSV文件的合并方案(Perl脚本修改+其他方法)
嘿,这个问题我太熟了!合并不同行数量的CSV核心就是要处理「某文件已经读完但其他文件还有内容」的情况,得给缺失的行补上对应数量的空字段,保证最终的CSV格式合规。下面分Perl脚本修改和几个更省心的替代方法来讲:
一、修改原Perl脚本适配不同长度CSV
假设你原来的Perl脚本是同步逐行读取多个文件的方式,这种方式在文件长度不一致时会提前终止。我们改成先把所有文件内容预读到内存,再按最长文件的行数逐行拼接,同时处理缺失行的空字段填充:
修改后的Perl脚本示例
#!/usr/bin/perl use strict; use warnings; # 替换成你要合并的CSV文件列表 my @files = qw(file1.csv file2.csv file3.csv); my @file_data; my $max_rows = 0; # 第一步:读取所有文件的内容,记录每个文件的行数和总行数最大值 foreach my $file (@files) { open my $fh, '<', $file or die "打不开文件 $file: $!"; my @lines = <$fh>; chomp @lines; close $fh; push @file_data, \@lines; $max_rows = scalar @lines if scalar @lines > $max_rows; } # 第二步:输出表头(默认所有文件表头一致,取第一个文件的表头) print $file_data[0][0], "\n"; # 第三步:逐行合并,缺失行补对应数量的空字段 for my $row_idx (1..$max_rows-1) { # 跳过表头,从数据行开始 my @merged_fields; foreach my $data (@file_data) { if ($row_idx < scalar @$data) { # 当前文件有这一行,直接拆分字段 push @merged_fields, split /,/, $data->[$row_idx]; } else { # 当前文件无此行,按表头字段数补空 my $field_count = scalar split /,/, $file_data[0][0]; push @merged_fields, ('') x $field_count; } } print join(',', @merged_fields), "\n"; }
关键修改说明
- 预读取所有内容:不再同步逐行读,而是把每个文件的所有行存到数组里,这样能明确知道每个文件的总行数。
- 空字段精准填充:当某个文件行数不足时,根据表头的字段数量填充对应个数的空字符串,避免CSV格式错乱。
- 如果你要处理带逗号的复杂字段(比如用引号包裹的
"张三,男"),上面的split /,/会出错,建议用Perl的Text::CSV模块(先通过cpan Text::CSV安装),用$csv->getline($fh)来读取行,拆分字段更严谨。
二、其他更简便的实现方法
如果不想折腾Perl脚本,这些方案可能更省心:
1. Python + Pandas(最推荐,处理复杂场景稳得一批)
Pandas会自动对齐表头,不同长度的文件缺失行自动填充NaN,你可以一键转成空字符串:
import pandas as pd import glob # 获取当前目录下所有CSV文件 csv_files = glob.glob('*.csv') # 批量读取所有CSV并合并 df_list = [pd.read_csv(file) for file in csv_files] merged_df = pd.concat(df_list, ignore_index=True) # 把缺失值替换为空字符串,保存为新CSV merged_df.fillna('', inplace=True) merged_df.to_csv('merged.csv', index=False)
这个方法不用手动处理字段数、行长度,甚至带引号的复杂字段也能完美识别。
2. Awk命令行工具(适合快速处理简单CSV)
用Awk记录每个文件的行,然后遍历到最长行数,自动补空字段:
BEGIN { file_count = ARGC - 1 # 读取所有文件的行到数组 for (i=1; i<=file_count; i++) { while ((getline line < ARGV[i]) > 0) { file_lines[i][++line_count[i]] = line } close(ARGV[i]) max_rows = (line_count[i] > max_rows) ? line_count[i] : max_rows } # 输出表头 print file_lines[1][1] # 逐行合并 for (row=2; row<=max_rows; row++) { merged = "" for (i=1; i<=file_count; i++) { if (row <= line_count[i]) { merged = merged (merged == "" ? "" : ",") file_lines[i][row] } else { # 按表头字段数补空 split(file_lines[1][1], fields, ",") empty = "" for (f=1; f<=length(fields); f++) { empty = empty (f==1 ? "" : ",") "" } merged = merged (merged == "" ? "" : ",") empty } } print merged } }
使用方式:把上面的代码存成merge_csv.awk,然后执行awk -f merge_csv.awk file1.csv file2.csv > merged.csv
内容的提问来源于stack exchange,提问作者Anthony
相关产品推荐
相关产品推荐

