2010-06-10 18 views
8

Estoy intentando escribir un script de Perl simple que lea un * .csv, coloque las filas del archivo * .csv en una matriz bidimensional, y luego imprima en el elemento fuera de la matriz y luego imprima una fila de la matriz .Cómo imprimir una matriz peridimensional 2?

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

Cuando ejecuto este script recibo las siguientes impresiones de error y nada:

No se puede utilizar de cadena ("1") como un ref ARRAY mientras que los "estrictos" árbitros están en uso al. /scripts.pl línea 16.

He buscado en varios otros foros y todavía no estoy seguro de cómo solucionar este problema. ¿Alguien puede ayudarme a arreglar este script?

+3

+1 para no desperdiciar el tiempo de todos omitiendo 'uso estricto; usa advertencias; '. – Ether

+1

Realmente debería evitar 2 argumentos 'abiertos'. Tiene una serie de problemas que es mejor evitar (para más información, consulte esta publicación de perlmonks de 2001: http://www.perlmonks.org/?node_id=131085) Vea esta publicación de SO para obtener información sobre los controles léxicos: http: // stackoverflow .com/questions/613906/why-does-programming-perl-use-local-not-my-for-filehandles – daotoad

+1

Ver ['perldoc perllol'] (http://perldoc.perl.org/perllol.html#Access e impresión) - Accediendo e imprimiendo – Zaid

Respuesta

10

No está creando una matriz bidimensional (un AoA o "Matriz de matrices" en Perl-parlance). Esta línea:

push(@table, @row); 

anexa los datos en @row-@table. Es necesario para empujar una referencia en su lugar, y crear una nueva variable cada vez a través del bucle de manera que no empujar la misma referencia en varias ocasiones:

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

durante el uso de split está bien para los archivos CSV triviales, es lamentablemente inadecuada para cualquier otra cosa. Utilizar un módulo como Text::CSV_XS lugar:

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 es la nomenclatura correcta. Realmente no hay tal cosa como una lista de listas en Perl. (A pesar del nombre de la página de manual de 'perllol. Suspiro). – friedo

+3

+1 para usar Text :: CSV; Realmente es tonto no. –

+0

@friedo: Punto tomado. He visto a LoL muchas veces, pero eso no es excusa para difundir conceptos erróneos. Fijo. –

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

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

impresiones

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

Editar: Voy a dejar a otros a obtener el crédito para controlar el empuje mal.

2

Se necesitan 2 cambios:

  1. uso variable local para la fila
  2. uso de las referencias de serie que puso en @table

modo que su programa debe mirar esto:

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

Quizás esto es lo que realmente quiere:

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

sería mejor usar la forma moderna de abrir: abrir (mi $ csv, '<', $ ARGV [0]) –

0

Cambio

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

Luego se imprime bien.

+0

... pero hace lo incorrecto de '@row 'no se crea dentro del ciclo. –

+0

... pero podría ser corregido si dices 'push @table, [@row]' para usar una referencia a una copia de '@ row'. – mob

2

Si llama de empuje con los argumentos de la lista, anexa la primera lista con la lista (s) restante en la pila de manera prudente. Lea acerca de push en Perldoc. Entonces su llamada de push(@table, @row); está creando una lista más larga @table, no una matriz bidimensional.

Ha recibido varias publicaciones que presionar una referencia de lista como @row como \@row creará una lista de filas, y de hecho eso funciona. Tiendo a hacerlo un poco diferente.Por supuesto, con Perl, ¡siempre hay otra manera de hacerlo!

Sintácticamente, también puede insertar una referencia de matriz anónima en el elemento escalar de una lista para crear una lista de múltiples dimensiones. Lo más importante que debe saber sobre las referencias en Perl es: 1) son escalares y 2) pueden referirse a cualquier cosa en Perl: código, matriz, hash, otra referencia. Pase algún tiempo con el Perl Ref Tutorial y esto será más claro. Con su código, simplemente agregue [ ] alrededor del elemento que desea que sea la 2da dimensión en su lista, así que push(@table, @row); debe ser push(@table, [ @row ]); En el mismo sentido, usted pone [ ] alrededor de su división para que se convierta en push(@table, [ split(/\s*,\s*/, $_) ]); Esto simultáneamente realizará la división y creará una matriz anónima para el resultado.

El problema específico que tiene, cómo crear y acceder a una lista multidimensional, también se trata muy bien en Tom Christensen's perllol tutorial Las soluciones a sus problemas específicos con su código se tratan directamente aquí.

volver a escribir el código con el código exacto del ejemplo de Tom en perllol, se convierte en esto:

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