2010-06-10 8 views
8

Ich versuche, ein einfaches Perl-Skript zu schreiben, liest ein * .csv, legt die Zeilen der * .csv-Datei in einem zweidimensionalen Array, und druckt dann auf Element aus dem Array und druckt dann eine Zeile des Arrays .Wie drucke ich ein Perl 2-dimensionales Array?

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

Als ich dieses Skript ich die folgenden Fehler und keine Druckausgabe erhalten:

nicht Zeichenfolge verwenden können („1“) als ARRAY ref während „strict refs“ im Einsatz bei. /scripts.pl Zeile 16.

Ich habe in einer Reihe von anderen Foren gesucht und bin immer noch nicht sicher, wie Sie dieses Problem beheben. Kann mir jemand helfen, dieses Skript zu reparieren?

+3

+1 für nicht jedermanns Zeit zu verschwenden durch Weglassen 'use strict; Verwenden Sie Warnungen; '. – Ether

+1

Sie sollten wirklich vermeiden 2 Argument 'offen'. Es gibt eine Reihe von Problemen, die am besten zu vermeiden sind (zur Diskussion siehe diesen perlmonks-Beitrag von 2001: http://www.perlmonks.org/?node_id=131085) Siehe diesen SO-Post für Informationen zu lexikalischen Handles: http: // stackoverflow .com/questions/613906/why-does-programming-perl-verwenden-lokal-nicht-meine-für-filehandles – daotoad

+1

Siehe [perldoc perllol] (http://perldoc.perl.org/perllol.html#Access -und-Drucken) - Zugriff und Drucken – Zaid

Antwort

10

Sie erstellen kein zweidimensionales Array (ein AoA oder "Array of Arrays" in Perl-Sprache). Diese Zeile:

push(@table, @row); 

fügt die Daten in @row-@table. Sie müssen einen Verweis stattdessen drücken, und eine neue Variable jedes Mal durch die Schleife erstellen, damit Sie wiederholen nicht die gleiche Referenz drücken:

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

Bei der Verwendung von split für triviale CSV-Dateien in Ordnung, es ist völlig unzureichend für alles andere. Verwenden Sie ein Modul wie Text::CSV_XS statt:

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 ist die korrekte Nomenklatur. In Perl gibt es wirklich keine Liste mit Listen. (Der Name der "perllol" Manpage ungeachtet. Seufz.) – friedo

+3

+1 für die Verwendung von Text :: CSV; es ist wirklich albern, nicht. –

+0

@friedo: Punkt genommen. Ich habe LoL oft benutzt, aber das ist keine Entschuldigung für die Verbreitung von Missverständnissen. Fest. –

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

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

druckt

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

Edit: Ich werde andere lassen den Kredit für den Fang von den falschen Push bekommen.

2

Sie müssen zwei Änderungen:

  1. Verwendung lokale Variable für Zeile
  2. Verwendung Referenzen für Array setzen Sie in @table

Also Ihr Programm diese aussehen sollte:

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

Vielleicht ist das, was Sie eigentlich wollen:

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

es wäre besser, moderne Form von open zu verwenden: open (my $ csv, '<', $ ARGV [0]) –

0

ändern

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

Dann druckt es ok aus.

+0

... aber tut das Falsche von '@row 'wird nicht innerhalb der Schleife erstellt. –

+0

... aber es könnte behoben werden, wenn Sie 'push @table, [@row]' sagen, um einen Verweis auf eine Kopie von '@ row' zu verwenden. – mob

2

Wenn Sie anrufen Push mit Liste Argumente, fügen Sie die erste Liste mit der verbleibenden Liste (n) in Stapel weise Art und Weise. Lesen Sie über Push bei Perldoc. Ihr Aufruf von push(@table, @row); erstellt also eine längere @table Liste, kein zweidimensionales Array.

Sie haben mehrere Posts erhalten, die beim Drücken einer Listenreferenz auf @row als \@row eine Liste von Zeilen erstellen, und das funktioniert tatsächlich. Ich tendiere dazu, es ein bisschen anders zu machen.Bei Perl gibt es natürlich immer einen anderen Weg!

Syntaktisch können Sie auch einen anonymen Array-Verweis in das Skalarelement einer Liste verschieben, um eine Liste mit mehreren Dimensionen zu erstellen. Das Wichtigste, was man über Referenzen in Perl wissen sollte, ist: 1) sie sind ein Skalar und 2) sie können sich auf alles in Perl beziehen - Code, Array, Hash, eine andere Referenz. Verbringe etwas Zeit mit dem Perl Ref Tutorial und dies wird klarer werden. Fügen Sie einfach [ ] mit Ihrem Code um das Element hinzu, das die 2. Dimension in Ihrer Liste sein soll, also push(@table, @row); sollte push(@table, [ @row ]); sein. Im selben Sinne setzen Sie [ ] um Ihren Split, so dass es push(@table, [ split(/\s*,\s*/, $_) ]); wird Dies wird gleichzeitig die Aufteilung und erstellen ein anonymes Array zum Ergebnis.

Das spezielle Problem, das Sie haben, wie Sie eine multidimensionale Liste erstellen und aufrufen, wird auch in Tom Christensens perllol tutorial sehr gut behandelt. Die Lösungen für Ihre spezifischen Probleme mit Ihrem Code werden hier direkt behandelt.

Umschreiben von Code mit dem genauen Code von Tom Beispiel in perllol, wird es dies:

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