Perl CSV转换为哈希表

7

我有一个包含标题行和数据之前的注释文本的CSV文件,我想将其作为哈希读取以进行进一步操作。哈希的主键将是两个数据值的组合。我该怎么做?

  1. 使用模式“index”搜索标题行
  2. 使用标题作为键
  3. 读入文件的其余部分。

示例CSV:

#
#
#
#
Description information of source of file.

index,label,bit,desc,mnemonic
6,370,11,three,THRE
9,240,23,four,FOR
11,120,n/a,five,FIV

期望哈希值示例

( '37011' => { 'index' => '6', 'label' => '370', 'bit' => '11', 'desc' => 'three', 'mnemonic' => 'THRE'}, '24023' => {'index' => '9', 'label'  => '240', 'bit' => '23', 'desc' => 'four', 'mnemonic' => 'FOR'}, '120n/a' => {'index' => '11', 'label'  => '120', 'bit' => 'n/a', 'desc' => 'five', 'mnemonic' => 'FIV'} )   
4个回答

12
您需要使用Text::CSV模块来完成此操作:
#!/usr/bin/env perl
use strict;
use warnings;
use Data::Dumper;
use Text::CSV;

my $filename = 'test.csv';

# watch out the encoding!
open(my $fh, '<:utf8', $filename)
    or die "Can't open $filename: $!";

# skip to the header
my $header = '';
while (<$fh>) {
    if (/^index,/x) {
        $header = $_;
        last;
    }
}

my $csv = Text::CSV->new
    or die "Text::CSV error: " . Text::CSV->error_diag;

# define column names    
$csv->parse($header);
$csv->column_names([$csv->fields]);

# parse the rest
while (my $row = $csv->getline_hr($fh)) {
    my $pkey = $row->{label} . $row->{bit};
    print Dumper { $pkey => $row };
}

$csv->eof or $csv->error_diag;
close $fh;

Text::CSV::Simple 使这变得更加简单。 - Will Sheppard

3
您可以像这样做:

您始终可以采取以下措施:

#!/usr/bin/env perl

use strict;
use warnings;

my %hash;
while( <DATA> ){ last if /index/ } # Consume the header
my $labels = $_;  # Save the last line for hash keys
chop $labels;
while(<DATA>){
    chop;
    my @a = split ',';
    my $idx = 0;
    my %h = map { $_ => $a[$idx++]} split( ",", $labels );
    $hash{ $a[1] . $a[2] } = \%h;
}

while( my ( $K, $H ) = each %hash ){
    print "$K :: ";
    while( my( $k, $v ) = each( %$H ) ) {
        print $k . "=>" . $v . "  ";
    }
    print "\n";
}

__DATA__

#
#
#
#
Description information of source of file.

index,label,bit,desc,mnemonic
6,370,11,three,THRE
9,240,23,four,FOR
11,120,n/a,five,FIV

我同意...如果你知道你的输入格式,就不需要调用笨重的模块和成千上万行的代码。 - Erik Aronesty

2
自2005年以来,Text::CSV::Simple已经存在。
从文档中可以看到:
# Map the fields to a hash
my $parser = Text::CSV::Simple->new;
$parser->field_map(qw/id name null town/);
my @data = $parser->read_file($datafile);

...简单!


1
简单易用的解析器
sub parse_csv {
    my ($f, $s, %op) = @_;  # file, sub, options
    my $d = $op{delim}?$op{delim}:"\t";  # delimiter, can be a regex
    open IN, $f; 
    $_=<IN>; chomp;
    my @h=map {s/"//g; lc} split /$d/; # header assumed, could be an option
    $h[0]="id" if $h[0] eq ""; # r compatible
    while(<IN>) {
        chomp;
        my @d=split /$d/;
        map {s/^"//; s/"$//;} @d; # any file with junk in it should fail anyway
        push @h, "id" if (@h == (@d - 1)); # r compat
        my %d=map {$h[$_]=>$d[$_]} (0..$#d);
        &{$s}(\%d);
    }
}

使用示例:

parse_csv("file.txt", sub {
   die Dumper $_[0];
})

请注意,类似于$.,和$_这样的内容仍然可以在子程序中使用。

杰作!你能否简单解释一下,比如 push @h, "id" if (@h == (@d - 1)); 这行代码是做什么的?还有 &{$s}(\%d); 这行代码呢?lc 是什么意思?r compatible 是什么意思? - msciwoj

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接