2013-03-08 17 views
5

Ho un file CSV che contiene testo di commento prima della riga di intestazione e dati, che vorrei leggere come hash per ulteriori manipolazioni. La chiave primaria ha l'hash sarà una combinazione di due valori di dati. Come posso?Perl CSV to hash

  1. Cercare la riga di intestazione usando il modello della 'indice'
  2. Usa intestazione per le chiavi
  3. legge nel resto del file.

Esempio 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 

Esempio desiderato hash

('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'}) 

risposta

9

È necessario il modulo Text::CSV per questo:

#!/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; 
+0

[Testo :: CSV :: Semplice] (https://metacpan.org/pod/Text::CSV::Simple) lo rende ancora più semplice. –

3

Si può sempre fare qualcosa di simile:

#!/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 
+0

Accetto ... se si conosce il formato di input, non è necessario richiamare moduli sgraziati e migliaia di righe di codice. –

1

semplice, parser pasteable

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); 
    } 
} 

esempio di utilizzo:.

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

nota che roba come $, e $ _ ancora lavorare nel sub

+0

capolavoro! Potresti dare poche parole di spiegazione, ad esempio cosa dice line 'push @h," id "if (@h == (@d - 1));' o line' & {$ s} (\% d); ' fare? cosa significa 'lc'? cosa intendi con 'r compatible'? – msciwoj

1

Text::CSV::Simple esiste dal 2005 ...

Dalla documentazione:

# 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); 

... semplice!