如何将行合并到一个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] 表示仅分配给尚未定义的元素。