4

我已将最常见的 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"...],

};
4

3 回答 3

5

您正在尝试在 push 语句中使用切片,但这是行不通的。该数组将处于标量上下文中,这就是您看到 key 的原因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;
于 2013-07-03T11:02:30.627 回答
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 号员工的职位称为 is,$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 文件,甚至可以帮助您创建这些结构(我已经很久没有使用它了,所以我不记得它所做的一切)。但是,我相信您会发现使您的员工结构成为哈希哈希,初始哈希以员工编号为键,而子哈希以字段为键,比数组哈希好得多。

于 2013-07-03T12:03:43.430 回答
1

重写这一行

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

至:

foreach my $columnName (@columnNames) {
    my $columnValue = shift @columnValues;
    push @{ $hash{$columnName} }, $columnValue;
}
于 2013-07-03T10:38:19.147 回答