【问题标题】:Perl to group columns in csv with multilinesPerl 用多行对 csv 中的列进行分组
【发布时间】:2018-03-01 03:32:36
【问题描述】:

我有一个带有多行的 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 脚本(读取 hash 中的前 3 个字段作为 key,hash 中的最后一个字段作为 value,通过 join 打印到 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 解决方案也很感激。

【问题讨论】:

  • 您希望哈希键是一个数组吗?您当前只是使用数组的最后一个成员。
  • 是的,我确实希望使用数组切片:@flds[0..2] 作为哈希键。不正确吗?我还尝试分配@fldsSlc=@flds[0..2],并将数组@fldsSlc 分配为哈希键,同样的问题。

标签: perl csv hash


【解决方案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 取回原始值。

【讨论】:

    猜你喜欢
    • 2021-12-23
    • 1970-01-01
    • 1970-01-01
    • 2015-08-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多