【问题标题】:Parsing a CSV file using array of hash of hashes in Perl使用 Perl 中的哈希数组解析 CSV 文件
【发布时间】:2016-04-20 22:25:49
【问题描述】:

我有这种形式的 CSV 数据:

Sl.No, Label, Type1, Type2...
1, "label1", Y, N, N...
2, "label2", N, Y, Y...
...

其中“Y”和“N”表示是否将相应的标签打印到文件中。

while ( <$fh> ) {    #Reading the CSV file

    $filter = $_;
    chomp $filter;
    $filter =~ tr/\r//d;

    if ( $. == 1 ) {
        @fieldNames = split ",", $filter;
    }
    else {
        @fields = split ",", $filter;
        $numCustomers = scalar(@fields) - 2;
        push @labels, $fields[2];

        for ( $i = 0; $i < $numCustomers; $i++ ) {

            for ( $j = 0; $j < scalar(@labels); $j++ ) {
                $customer[$i][$j] = $fields[ 2 + $i ];
            }

            $custFile = "customer" . $i . "_external.h";

            open( $fh1, ">", $custFile ) or die "Unable to create external header file for customer $i";
        }
    }
}

for ( $i = 0; $i < scalar(@labels); $i++ ) {

    for ( $j = 0; $j < $numCustomers; $j++ ) {

        $Hash{ $fieldNames[ 2 + $i ] }->{ $labels[$i] } = $customer[$j][$i];
        push @aoh, %Hash;    #Array of hashes
    }
}

my @headerLines = read_file($intFile);  # read the internal file, and copy only
                                        # those lines that are not marked with
                                        # "N" in the CSV file to the external file.

# iterate over elements of each hash and print the labels only if value is 'Y'

foreach my $headerLine (@headerLines) {

    chomp $headerLine;

    for $i ( 0 .. $#aoh ) {

        for my $cust1 ( sort keys %{ $aoh[$i] } ) {    #HERE

            for my $reqLabel1 ( keys %{ $aoh[$i]{$cust1} } ) {

                print "$cust1, $reqLabel1 : $aoh[$i]{$cust1}{$reqLabel1}\n";

                if ( $aoh[$i]{$cust1}{$reqLabel1} eq "Y" ) {

                    for ( $j = 0; $j < $numCustomers; $j++ ) {
                        $req[$j][$i] = $reqLabel1;
                    }
                }
                else {
                    for ( $j = 0; $j < $numCustomers; $j++ ) {
                        $nreq[$j][$i] = $reqLabel1;
                    }
                }
            }

        }

        if ( grep { $headerLine =~ /$_/ } @nreq ) {
            next;    #Don't print this line in the external file
        }
        else {
            print $fh1 $headerLine . "\n";    #print this line in the external file
        }
    }
}

这抱怨“不能使用字符串 Type1 作为哈希 REF”,指的是标记为 #HERE 的行。

我尝试过到处转储数据结构,但我不确定这是从哪里出现的。

任何见解将不胜感激。

我收到反馈说使用Text::CSV 会是更好的解决方案。它将如何减少使用嵌套数据结构的需要?

【问题讨论】:

  • 给定样本输入的预期输出是什么?
  • $intFile中的行被写入外部文件(print $fh1 $headerLine."\n";);除非该行在 CSV 中标记为“N”。
  • CSV 中的每个“类型”都有一个单独的外部文件。
  • 你到底想在这里完成什么?因为我会指出 - Text::CSV 支持命名列,所以你实际上不必为哈希和切片而烦恼。
  • 总是use strict; use warnings;

标签: perl csv file-io hash


【解决方案1】:

好的,使用Text::CSV,您的问题会变得容易得多。我建议看一下重写,或者重新提出你的问题。

但你的问题其实是这样的:

push @aoh, %Hash;                #Array of hashes

这根本不会创建散列数组。这会从%Hash 中提取所有元素(不按特定顺序,除了键和值配对)并将它们插入@aoh

可能想要:

push @aoh, \%Hash;

或许:

push @aoh, { %Hash }; 

我不是很清楚,因为你在重复使用%Hash,所以你可能会得到重复。这最好由use strict; use warnings; 处理,并在词法上正确地确定您的哈希值。

【讨论】:

  • 我接受使用Text::CSV 会是一个更清洁的解决方案,但我想睁大眼睛走出这个烂摊子。我理解我在那里犯的错误,并且我使用了strictwarnings。您能否详细说明正确地对我的哈希进行词法范围界定?我将不胜感激。
  • 好吧,因为您没有声明 %Hashmyperl 将其视为全局变量。所以每次你这样做时:$Hash{$fieldNames[2 + $i]}-&gt;{$labels[$i]} = $customer[$j][$i]; 你向现有的哈希添加一个新元素。但是不要删除旧的,所以如果你此时尝试将它插入到你的数组中,你就会被骗。如果您将哈希范围限定为循环(使用my),那么它会在每次迭代时创建一个 new 数据结构。
  • 我想我明白了。谢谢。
  • Data::Dumper 对于这类事情是无价的。
  • 绝对。我对print Dumper(....) 的评论比其他任何东西都多。 ;)
【解决方案2】:

我只保留一组打开的文件句柄(如果没有太多类型)并在逐行读取文件时打印到它们。

#!/usr/bin/perl
use warnings;
use strict;

chomp( my $header = <> );
my @names = split /, /, $header;

my @handles;
for my $type (@names[ 2 .. $#names ]) {
    open my $fh, '>', $type or die "$type: $!";
    push @handles, $fh;
}

while (<>) {
    chomp;
    my @fields = split /, /;
    for my $index (0 .. $#handles) {
        print { $handles[$index] } $fields[1], "\n" if 'Y' eq $fields[ $index + 2 ];
    }
}

我使用以下输入来测试它:

Sl.No, Label, Type1, Type2, Type3, Type4
1, "label1", Y, N, Y, N
2, "label2", N, Y, Y, N

如果您的输入包含\r 行结束,请将binmode 设置为:crlf

【讨论】:

  • 我显然遗漏了一些东西 - 你能解释一下chomp( my $header = &lt;&gt; );吗?
  • &lt;&gt; 在标量上下文中从输入中读取一行,chomp 删除最后的换行符。
  • 在打印到输出文件之前,我还需要检查这些行(即使标记为“Y”)是否存在于另一个(“内部”)文件中。
  • @AnupamaG:首先对内部文件进行哈希处理。
  • 如何读取我现在写入的文件?以下是我所做的,基于您告诉我的内容:$numCustomers = scalar(@names) - 2; for(my $i = 0; $i &lt; $numCustomers; $i++) { my $custFile = "customer".$i."_external.h"; open my $fh, '&gt;', $custFile or die "$custFile: $!"; push @handles, $fh; push @files, $custFile; } 之后:for(my $i = 0; $i &lt; $numCustomers; $i++) { my @Lines = read_file($files[$i]); #do something } 这个 read_file 不起作用(我正在使用 File::Slurp)。你能告诉我为什么吗?
猜你喜欢
  • 2018-07-04
  • 1970-01-01
  • 1970-01-01
  • 2013-03-06
  • 2021-02-27
  • 2015-01-22
  • 1970-01-01
  • 1970-01-01
  • 2017-05-29
相关资源
最近更新 更多