2010-06-10 17 views
8

Sto provando a scrivere un semplice script Perl che legge un * .csv, posiziona le righe del file * .csv in un array bidimensionale, quindi stampa sull'elemento fuori dall'array e quindi stampa una riga dell'array .Come stampare una matrice bidimensionale Perl?

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

open(CSV, $ARGV[0]) || die("Cannot open the $ARGV[0] file: $!"); 
my @row; 
my @table; 

while(<CSV>) { 
     @row = split(/\s*,\s*/, $_); 
     push(@table, @row); 
} 
close CSV || die $!; 

foreach my $element (@{ $table[0] }) { 
    print $element, "\n"; 
} 

print "$table[0][1]\n"; 

Quando eseguo questo script ricevo il seguente errore stampe e niente:

Impossibile utilizzare stringa ("1") come un riferimento ad array, mentre "strict refs" in uso presso. /scripts.pl riga 16.

Ho cercato in un numero di altri forum e non sono ancora sicuro su come risolvere questo problema. Qualcuno può aiutarmi a risolvere questo script?

+3

+1 per non sprecare il tempo di tutti omettendo 'use strict; usare avvertenze; ​​'. – Ether

+1

Dovresti davvero evitare 2 argomenti 'apri'. Ha un numero di problemi che è meglio evitare (per la discussione vedi questo post perlmonks del 2001: http://www.perlmonks.org/?node_id=131085) Vedi questo post SO per informazioni sugli handle lessicali: http: // stackoverflow .com/questions/613906/why-does-programming-perl-use-local-not-my-filehandles – daotoad

+1

Vedere ['perldoc perllol'] (http://perldoc.perl.org/perllol.html#Access -e-Stampa) - Accesso e stampa – Zaid

risposta

10

Non si sta creando un array bidimensionale (un AoA o "Array of Arrays" in Perl-parlance). Questa linea:

push(@table, @row); 

aggiunge i dati in @row-@table. È necessario spingere un riferimento, invece, e creare una nuova variabile di volta in volta attraverso il ciclo in modo da non spingere lo stesso riferimento più volte:

my @table; 
while(<CSV>) { 
    my @row = split(/\s*,\s*/, $_); 
    push(@table, \@row); 
} 

durante l'utilizzo split va bene per i file CSV banali, è del tutto inadeguato per qualsiasi altra cosa Utilizzare un modulo come Text::CSV_XS invece:

use strict; 
use warnings; 
use Text::CSV_XS; 

my $csv = Text::CSV_XS->new() or die "Can't create CSV parser.\n"; 
my $file = shift @ARGV   or die "No input file.\n"; 
open my $fh, '<', $file  or die "Can't read file '$file' [$!]\n"; 

my @table; 
while (my $row = $csv->getline($fh)) { 
    push @table, $row; 
} 
close $fh; 

foreach my $row (@table) { 
    foreach my $element (@$row) { 
     print $element, "\n"; 
    } 
} 

print $table[0][1], "\n"; 
+0

Array-of-array è la nomenclatura corretta. In realtà non esiste una lista di liste in Perl. (Nonostante il nome della manpage 'perllol'. Sigh.) – friedo

+3

+1 per l'utilizzo di Text :: CSV; è davvero stupido non farlo. –

+0

@friedo: punto preso. Ho visto LoL usato molte volte ma non è una scusa per diffondere idee sbagliate. Fisso. –

3
my @arr = ([a, b, c], 
      [d, e, f], 
      [g, h, i], 
     ); 

for my $row (@arr) { 
    print join(",", @{$row}), "\n"; 
} 

stampe

a,b,c 
d,e,f 
g,h,i 

Edit: Ti lascio gli altri a ottenere il credito per la cattura la spinta sbagliata.

2

avete bisogno di 2 modifiche:

  1. uso variabile locale per la riga
  2. uso riferimenti per serie si mette in @table

Così il vostro programma dovrebbe guardare questo:

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

open(CSV, $ARGV[0]) || die("Cannot open the $ARGV[0] file: $!"); 
my @table; 

while(<CSV>) { 
    my @row = split(/\s*,\s*/, $_); 
    push(@table, \@row); 
} 
close CSV || die $!; 

foreach my $element (@{ $table[0] }) { 
    print $element, "\n"; 
} 

print "$table[0][1]\n";  
1

Forse è proprio quello che vuoi:

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

open(CSV, $ARGV[0]) || die("Cannot open the $ARGV[0] file: $!"); 
my @table; 

while(<CSV>) { 
     my @row = split(/\s*,\s*/, $_); 
     push(@table, \@row); 
} 
close CSV || die $!; 

foreach my $element (@{ $table[0] }) { 
    print $element, "\n"; 
} 

print "$table[0][1]\n"; 
+0

sarebbe meglio usare la forma moderna di open: open (my $ csv, '<', $ ARGV [0]) –

0

Change

#push(@table, @row); 
push(@table, \@row); #push a reference to the array into each cell in @table. 

quindi stampa ok.

+0

... ma fa la cosa sbagliata di '@row 'non è stato creato all'interno del ciclo. –

+0

... ma potrebbe essere corretto se si dice 'push @table, [@row]' per usare un riferimento a una copia di '@ row'. – mob

2

Se si chiama spinta con argomenti della lista, si aggiunge la prima lista con l'elenco rimanente (s) in pila saggio di moda. Leggi informazioni su push allo Perldoc. Quindi la tua chiamata di push(@table, @row); sta creando un elenco più lungo @table, non un array bidimensionale.

Hai ricevuto diversi post che spingono un riferimento a @row come \@row creerà un elenco di righe, e in effetti funziona. Tendo a farlo un po 'diversamente.Ovviamente, con Perl, c'è sempre un altro modo per farlo!

Sintatticamente, è anche possibile inserire un riferimento anonimo dell'array nell'elemento scalare di un elenco per creare un elenco a più dimensioni. La cosa più importante da sapere sui riferimenti in Perl è: 1) sono uno scalare e 2) possono riferirsi a qualsiasi cosa in Perl - codice, array, hash, un altro riferimento. Passa un po 'di tempo con lo Perl Ref Tutorial e questo diventerà più chiaro. Con il tuo codice, aggiungi [ ] attorno all'elemento che vuoi essere la seconda dimensione nell'elenco, quindi push(@table, @row); dovrebbe essere push(@table, [ @row ]); Nello stesso senso, devi inserire [ ] intorno allo split in modo che diventi lo push(@table, [ split(/\s*,\s*/, $_) ]); allo stesso tempo. una matrice anonima al risultato.

Il problema specifico che si ha, come creare e accedere a un elenco multidimensionale, è anche trattato molto bene in perllol tutorial di Tom Christensen. Le soluzioni ai problemi specifici con il codice vengono trattate direttamente qui.

riscrivere il codice con il codice esatto dall'esempio di Tom in perllol, diventa in questo modo:

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

my (@row, @table, $n, $rowref); 

while(<DATA>) { 
     chomp; 
     # regex to separate CSV (use of a cpan module for CSV STRONGLY advised... 
     @row = /(?:^|,)("(?:[^"]+|"")*"|[^,]*)/g; 
     for (@row) { 
      if (s/^"//) { s/"$//; s/""/"/g; } 
     } 
     push(@table, [ @row ]); #Note the [ ] around the list 
} 

# Now the table is created, print it: 
my $rowcnt=0; 
foreach $rowref (@table) { 
    print "row $rowcnt:\n"; 
    $rowcnt++; 
    print " [ @$rowref ], \n"; 
} 

# You can access the table in the classic [i][j] form: 
for my $i (0 .. $#table) { 
    $rowref = $table[$i]; 
    $n = @$rowref - 1; 
    for my $j (0 .. $n) { 
     print "element $i, $j of table is $table[$i][$j]\n"; 
    } 
} 

# You can format it: 
for my $i (0 .. $#table) { 
    print "$table[$i][0] $table[$i][1]\n"; 
    print "$table[$i][2]\n"; 
    print "$table[$i][3], $table[$i][4] $table[$i][5]\n\n"; 
} 


__DATA__ 
Mac,Doe,120 jefferson st.,Riverside, NJ, 08075 
Jack,McGinnis,220 hobo Av.,Phila, PA,09119 
"John ""Da Man""",Repici,120 Jefferson St.,Riverside, NJ,08075 
Stephen,Tyler,"7452 Terrace ""At the Plaza"" road",SomeTown,SD, 91234 
,Blankman,,SomeTown, SD, 00298 
"Joan ""Joan, the bone""",Jett,"9th, at Terrace plc",Desert City,CO,0
Problemi correlati