如何将行合并到一个csv文件中
How to merge lines into a csv file
我有一个包含多行的 csv 文件,其中第一个字段是重复的,而其他字段中只有一个实际填充(并且总是不同)
我需要编写一个 perl CGI 将这些行合并为一个
csv 文件如下例所示
aaaa,3
aaaa,,5
aaaa,,,1
aaaa,,,,3
bb,2
bb,,4
bb,,,,,,,,,,6
cc,,,,,,5
dd,5
我想要的输出应该是这样的:
aaaa,3,5,1,3
bb,2,4,,,,,,,,6
cc,,,,,,5
dd,5
我对 perl 语言还很陌生,但通过一些研究,我确实认为使用哈希似乎是可行的方法,但我仍然没有完全理解它们是如何工作的,请记住我需要它在perl cgi,所以单个命令行不会帮助我
所以我从另一个 post here
抓取了这段代码
open my $ifh, '<', "input_file" or die $!;
open my $ofh, '>', "output_file" or die $!;
while (<$ifh>) {
chomp;
my @F = split /,/;
my $key = shift @F;
push @{$hash{$key}}, @F;
}
foreach (sort keys %hash) {
print $ofh "$_," . join (',', @{$hash{$_}}) . "\n";
}
close $ifh;
close $ofh;
但它只是在重复的同一行上添加字段,我需要在实际的未定义字段中写入
关于合并来自不同行的值这一点让我很感兴趣,可以尝试一下。这就是我所做的。其他人可能能够改进我的解决方案。
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my %data;
while (<DATA>) {
chomp;
my ($key, @values) = split /,/;
if ($data{$key}) {
# It seems we can be sure that the new value
# we're adding will be the last element on the line.
$data{$key}[$#values] = $values[$#values];
} else {
$data{$key} = \@values;
}
}
for my $k (sort keys %data) {
say join ',', $k, map { $_ // '' } @{$data{$k}};
}
__DATA__
aaaa,3
aaaa,,5
aaaa,,,1
aaaa,,,,3
bb,2
bb,,4
bb,,,,,,,,,,6
cc,,,,,,5
dd,5
我得到的输出是:
aaaa,3,5,1,3
bb,2,4,,,,,,,,6
cc,,,,,,5
dd,5
将其转换为使用 CGI 进行输入和输出留作 reader :-)
的练习
很好,但它依赖于同一密钥的每一行数据都比前面的所有数据长,你的问题不能保证这一点
这里有一个非常相似的程序可以解决这个问题,还可以跟踪键在源数据中出现的顺序,以便可以在输出中重现
此程序需要输入 CSV 文件的路径作为命令行上的参数,并将输出打印到 STDOUT。您可以在命令行上重定向输出
将其编写为 CGI 应用程序是一个过于宽泛的问题。你需要做你能做的,如果你遇到困难,请在这里问具体问题
use strict;
use warnings 'all';
my ( @keys, %data );
while ( <> ) {
next unless /\S/;
chomp;
my @newline = split /,/;
my $key = $newline[0];
if ( my $line = $data{ $key } ) { # Update any previously-blank fields
for my $i ( 0 .. $#newline ) {
$line->[$i] = $newline[$i] unless length $line->[$i] // '';
}
}
else { # Build the first instance of this key
$data{ $key } = \@newline;
push @keys, $key;
}
}
print join( ',', @{ $data{$_} } ), "\n" for @keys;
输出
aaaa,3,5,1,3
bb,2,4,,,,,,,,6
cc,,,,,,5
dd,5
我会像这样修改你的代码:
use strict;
use warnings;
my %hash;
while (<DATA>) {
chomp;
my @F = split ',';
my $key = $F[0];
for my $i (0..$#F) {
$hash{$key}->[$i] //= $F[$i];
}
}
for my $key (sort keys %hash) {
print join(',',@{$hash{$key}})."\n";
}
__DATA__
aaaa,3
aaaa,,5
aaaa,,,1
aaaa,,,,3
bb,2
bb,,4
bb,,,,,,,,,,6
cc,,,,,,5
dd,5
$#F
是最后一个元素的数组地址,所以(0..$#F)
是@F中地址的一个范围。这允许您将 @F 中的元素分配到另一个数组的相同地址(在这种情况下,$hash{$key}
是一个匿名数组,又名 arrayref,并且 ${$hash{$key}}[$i]
访问该数组的 $i 元素) .
$hash{$key}->[$i] //= $F[$i]
表示仅分配给尚未定义的元素。
我有一个包含多行的 csv 文件,其中第一个字段是重复的,而其他字段中只有一个实际填充(并且总是不同) 我需要编写一个 perl CGI 将这些行合并为一个 csv 文件如下例所示
aaaa,3
aaaa,,5
aaaa,,,1
aaaa,,,,3
bb,2
bb,,4
bb,,,,,,,,,,6
cc,,,,,,5
dd,5
我想要的输出应该是这样的:
aaaa,3,5,1,3
bb,2,4,,,,,,,,6
cc,,,,,,5
dd,5
我对 perl 语言还很陌生,但通过一些研究,我确实认为使用哈希似乎是可行的方法,但我仍然没有完全理解它们是如何工作的,请记住我需要它在perl cgi,所以单个命令行不会帮助我
所以我从另一个 post here
抓取了这段代码open my $ifh, '<', "input_file" or die $!;
open my $ofh, '>', "output_file" or die $!;
while (<$ifh>) {
chomp;
my @F = split /,/;
my $key = shift @F;
push @{$hash{$key}}, @F;
}
foreach (sort keys %hash) {
print $ofh "$_," . join (',', @{$hash{$_}}) . "\n";
}
close $ifh;
close $ofh;
但它只是在重复的同一行上添加字段,我需要在实际的未定义字段中写入
关于合并来自不同行的值这一点让我很感兴趣,可以尝试一下。这就是我所做的。其他人可能能够改进我的解决方案。
#!/usr/bin/perl
use strict;
use warnings;
use feature 'say';
my %data;
while (<DATA>) {
chomp;
my ($key, @values) = split /,/;
if ($data{$key}) {
# It seems we can be sure that the new value
# we're adding will be the last element on the line.
$data{$key}[$#values] = $values[$#values];
} else {
$data{$key} = \@values;
}
}
for my $k (sort keys %data) {
say join ',', $k, map { $_ // '' } @{$data{$k}};
}
__DATA__
aaaa,3
aaaa,,5
aaaa,,,1
aaaa,,,,3
bb,2
bb,,4
bb,,,,,,,,,,6
cc,,,,,,5
dd,5
我得到的输出是:
aaaa,3,5,1,3
bb,2,4,,,,,,,,6
cc,,,,,,5
dd,5
将其转换为使用 CGI 进行输入和输出留作 reader :-)
的练习
这里有一个非常相似的程序可以解决这个问题,还可以跟踪键在源数据中出现的顺序,以便可以在输出中重现
此程序需要输入 CSV 文件的路径作为命令行上的参数,并将输出打印到 STDOUT。您可以在命令行上重定向输出
将其编写为 CGI 应用程序是一个过于宽泛的问题。你需要做你能做的,如果你遇到困难,请在这里问具体问题
use strict;
use warnings 'all';
my ( @keys, %data );
while ( <> ) {
next unless /\S/;
chomp;
my @newline = split /,/;
my $key = $newline[0];
if ( my $line = $data{ $key } ) { # Update any previously-blank fields
for my $i ( 0 .. $#newline ) {
$line->[$i] = $newline[$i] unless length $line->[$i] // '';
}
}
else { # Build the first instance of this key
$data{ $key } = \@newline;
push @keys, $key;
}
}
print join( ',', @{ $data{$_} } ), "\n" for @keys;
输出
aaaa,3,5,1,3
bb,2,4,,,,,,,,6
cc,,,,,,5
dd,5
我会像这样修改你的代码:
use strict;
use warnings;
my %hash;
while (<DATA>) {
chomp;
my @F = split ',';
my $key = $F[0];
for my $i (0..$#F) {
$hash{$key}->[$i] //= $F[$i];
}
}
for my $key (sort keys %hash) {
print join(',',@{$hash{$key}})."\n";
}
__DATA__
aaaa,3
aaaa,,5
aaaa,,,1
aaaa,,,,3
bb,2
bb,,4
bb,,,,,,,,,,6
cc,,,,,,5
dd,5
$#F
是最后一个元素的数组地址,所以(0..$#F)
是@F中地址的一个范围。这允许您将 @F 中的元素分配到另一个数组的相同地址(在这种情况下,$hash{$key}
是一个匿名数组,又名 arrayref,并且 ${$hash{$key}}[$i]
访问该数组的 $i 元素) .
$hash{$key}->[$i] //= $F[$i]
表示仅分配给尚未定义的元素。