无法在perl中获得CSV格式的所需哈希值

时间:2013-07-03 10:17:03

标签: perl perl-data-structures

我已将最常见的emp表格导出为CSV格式。我想将每个列名称作为哈希键,并将值存储在数组中。

以下是代码

 use Data::Dumper;
    open($fh,"<EMP.csv");
    %hash = ();

    local $/= "\n";

    while(<$fh>){

         @columnNames = split(/,/,$_) if $. ==1;     
         @columnValues = split(/,/,$_);  
          push @{hash->{@columnNames}} ,@columnValues;       
    }

    print Dumper(\%hash);

当我尝试打印哈希时,我得到了这个

$VAR1 = {
          '8' => [
                   '"EMPNO"',
                   '"ENAME"',
                   '"JOB"',
                   '"MGR"',
                   '"HIREDATE"',
                   '"SAL"',
                   '"COMM"',
                   '"DEPTNO"
',
                   '"7839"',
                   '"KING"',
                   '"PRESIDENT"',
                   '""',
                   '"11/17/1981"',
                   '"5000"',
                   '""',
                   '"10"
',

但我期待这一点

$VAR1 = { '"EMPNO"'=>[12,3,4,5,6,7,8,9],
          '"EMPNAME"'=>["pavan","kumar"...],

};

3 个答案:

答案 0 :(得分:5)

您正尝试在推送语句中使用切片,但这不起作用。数组将处于标量上下文中,这就是您看到键8的原因。您需要循环键以将值推送到数组上。但是,为什么这样呢?

你可以使用Text::CSV模块,这很简单,可能更合适,假设你有一个真正的csv格式。

use strict;
use warnings;
use Data::Dumper;
use Text::CSV;

my $csv = Text::CSV->new({
        binary  => 1,
        eol     => $/,
    });
my %data;
open my $fh, "<", "yourfile.csv" or die $!;
$csv->column_names ($csv->getline($fh));          # get header names

while (my $row = $csv->getline_hr($fh)) {         # get hashref with values
    for my $key (keys %$row) {
        push @{$data{$key}}, $row->{$key};        # store values
    }
}
print Dumper \%data;

答案 1 :(得分:1)

重写此行

push @{hash->{@columnNames}} ,@columnValues; 

为:

foreach my $columnName (@columnNames) {
    my $columnValue = shift @columnValues;
    push @{ $hash{$columnName} }, $columnValue;
}

答案 2 :(得分:1)

这是你的问题:

push @{hash->{@columnNames}} ,@columnValues;

您尝试将@columnNames用作哈希中的键。 Perl会在标量上下文中自动获取它,因此,为8提供,因为数组中有八个值。

您要做的是将CSV中的第一行(包含列名称)视为特殊行,因为这些行将是您的数组的键。

my @column_names = read_csv_row; #Column names
my %employee_hash;
for my $column ( @column_names ) {
    $employee_hash{$column} = [];
}

这将为您提供一个哈希,按列名键入以引用数组。您现在必须读入CSV表的每一行,并将每个字段推送到正确的列散列中;

while ( my @employee_fields = read_csv_row ) {   #Your employee record is in @employee
    for my $field_num ( 0..$#employee_fields) {
        push @{ $employee_hash{$column_names[$field_num] }, $employee_fields[$field_num];
    }
}

这样做是从CSV行中取出每个字段并将其推入%employee_hash中正确的数组引用。我正在利用@column_names与每行的顺序相同。因此,$column_names[$field_number]是正确的哈希键,应该对应$employee_fields[$field_num]

但是,你在帖子中说的结构可能不是你真正想要的。你想要的是这样的:

%VAR = {
           7839 =>   {
                        ENAME    => "KING",
                        JOB      => "PRESIDENT",
                        MGR      => "",
                        HIREDATE => "11/17/1981",
                        SAL      => "5000",
                        COMM     => "",
                        DEPTNO   => "10",
                     }
      }

这将按照员工编号键入每个员工,并将所有相关员工字段作为该值的一部分。然后,您可以将员工编号7839的职称称为$employee{7839}->{JOB},该员工的姓名为$employee{7839}->{NAME}。这样,每个员工的所有信息都集中在一个记录中:

use warnings;
use strict;
use Data::Dumper;
use feature qw(say);

my @column_names = read_csv_row(); #Column name
my %employee_hash;
while ( my @minion_fields = read_csv_row() ) {   #Your employee record is in @employee
    my %minion_hash;
    my $minion_number = $minion_fields[0];
    for my $field_num ( 1..$#minion_fields) {
        $minion_hash{ $column_names[$field_num] } = $minion_fields[$field_num];
    }
    $employee_hash{$minion_number} = \%minion_hash;
}

sub read_csv_row {
    my $row = <DATA> or return;
    chomp $row;
    return split /,\s+/, $row;
}

say Dumper \%employee_hash;
__DATA__
empno, name, job, mgr, hiredate, sal, comm, deptno
7839, king, president, , 11/17/1981, 5000, , 10
1234, prince, vice-president, , 10/1/1980, 3000, , 10

顺便说一下,我还没有测试过这段代码。 (我现在就这样做,并进行必要的更正。)您可能想要使用Text::CSV,这将是一种更好的CSV文件阅读方式,甚至可以帮助您创建这些结构(我很久没有使用它,所以我不记得它做的一切)。但是,我相信你会发现你的员工结构是哈希的哈希值,其初始哈希值是由员工编号键入的,而字段键入的子哈希比数组的哈希要好得多。