我有一个带多线的csv(用,分隔)。 csv有4列,其中前3列包含多行文本,而group by发生在最后一列。
输入csv内容:/tmp/test.tmp.csv
"Total Sections",ota,4!n,01
"Input History",80,"HHMM28!c1!a[4!a]
6X
9X]",1
"T (MR)",17t,(MTR),02
"Input History",80,"HHMM28!c1!a[4!a]
6X
9X]",2
Reference,:4!t/1c,:(Text1)/(Text2),30
Reference,:4!t/1c,:(Text1)/(Text2),32
以上csv由6条记录组成,记录2和4由多条线组成。
预期输出(分组为空格):
"Total Sections",ota,4!n,01
"Input History",80,"HHMM28!c1!a[4!a]
6X
9X]",1 2
"T (MR)",17t,(MTR),02
Reference,:4!t/1c,:(Text1)/(Text2),30 32
我的 perl脚本(读取哈希中的前三个字段作为键,哈希中的最后一个字段作为值,使用连接打印到csv):
#!/usr/bin/perl -w
use strict;
use warnings;
use Text::CSV;
my %hash;
my @array;
my $in_qfn = "/tmp/test.tmp.csv";
my $out_qfn = "/tmp/test.out.tmp.csv";
# Li: Parsing multilines in csv
my $parser = Text::CSV->new({
binary => 1,
auto_diag => 1,
sep_char => ','
});
# Li: output multilines to csv
my $csvo = Text::CSV->new({
binary => 1,
eol => "\r\n",
sep_char => ','
});
open(my $data, '<:encoding(utf8)', $in_qfn) or die "Could not open $in_qfn: $!\n";
open(my $sts, '>:encoding(utf8)', $out_qfn) or die "Could not write $out_qfn: $!\n";
while (my $fields = $parser -> getline($data)) {
my $fz = $fields->[0];
my $fo = $fields->[1];
my $ft = $fields->[2];
my $fth = $fields->[3];
my @flds = ($fz, $fo, $ft, $fth);
# Li: push the first 3 columns as key and the last column as value
push(@{$hash{@flds[0..2]} }, $flds[3]);
}
# Li: print to output csv without join yet
for my $k (sort keys %hash) {
my @fldsAll = ($k, @{ $hash{$k}});
print("###LI### 1: key: $k, value: @fldsAll\n");
$csvo -> print($sts, \@fldsAll);
}
然而,脚本不能很好地工作,哈希键由于多行和可能的特殊字符而丢失,并且到处都没有双引号。
缺陷输出:
(MTR),02
4!n,01
:(Text1)/(Text2),30,32
"HHMM28!c1!a[4!a]
6X
9X]",1,2
有关如何修复它的任何想法?或者全新的perl解决方案也很受欢迎。
答案 0 :(得分:1)
您不能像使用数组那样使用数组作为哈希键,因为它不使用所有值,而只使用最后一个。
并且您不能使用对数组的引用,因为键与数组中的值无关。以此示例代码为例......
for($i=0;$i<3;$i++)
{
my @a=(1,2,3);
$hash{\@a}=10;
}
因为@a
的范围是循环的本地范围,所以最终会得到3个密钥。如果将my @a;
放在循环之外,最后会得到1个密钥。您可以更改数组的内容,它对密钥没有任何影响。
相反,您需要做的是将join
数组放入一个字符串中。
push(@{$hash{join("\t",@flds[0..2])} }, $flds[3]);
我使用了一个标签,但是任何3列中都不会出现的任何字符串都是您想要的,因此如果需要,您可以split
稍后获取原始值回来。