2010-06-10 5 views
8

Я пытаюсь написать простой скрипт Perl, который читает * .csv, помещает строки * .csv-файла в двухмерный массив, а затем печатает на элементе из массива и затем печатает строку массива ,Как печатать двумерный массив 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"; 

Когда я запускаю этот скрипт я получаю следующее сообщение об ошибке и не печатает:

Не можете использовать строку («1»), как ARRAY реф в то время как «жесткие» рефов в использовании в. /scripts.pl строка 16.

Я просмотрел несколько других форумов и до сих пор не знаю, как исправить эту проблему. Может ли кто-нибудь помочь мне исправить этот сценарий?

+3

+1 за не тратить время каждого опуская 'использовать строгий; использовать предупреждения; '. – Ether

+1

Вы должны действительно избегать 2 аргументов 'open'. У него есть ряд проблем, которые лучше всего избегать (для обсуждения см. Эту публикацию 2001 года perlmonks: http://www.perlmonks.org/?node_id=131085). См. Эту публикацию SO для информации о лексических дескрипторах: http: // stackoverflow .com/questions/613906/why-do-programming-perl-use-local-not-my-for-filehandles – daotoad

+1

См. ['perldoc perllol'] (http://perldoc.perl.org/perllol.html#Access -and-Printing). Доступ и печать – Zaid

ответ

10

Вы не создаете двумерный массив (AoA или «Массив массивов» в Perl-языке). Эта линия:

push(@table, @row); 

добавляет данные в @row к @table. Вам нужно нажать на ссылку вместо этого, и создать новую переменный каждый раз через петлю, так что вы не заставляйте ту же ссылку неоднократно:

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

При использовании split хорошо для тривиальных файлов CSV, это неадекватно для чего угодно. Используйте модуль как Text::CSV_XS вместо:

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

Массив массива - это правильная номенклатура. В Perl действительно нет такой вещи, как список списков. (Несмотря на название man-страницы 'perllol'. Вздох.) – friedo

+3

+1 для использования текста :: CSV; это действительно глупо, чтобы нет. –

+0

@friedo: Точка взята. Я видел LoL много раз, но это не повод для распространения неправильных представлений. Исправлена. –

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

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

печатает

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

Edit: я дам другим получить кредит для ловли неправильного толчка.

2

Вам необходимо 2 изменения:

  1. использовать локальную переменную для ряда
  2. ссылки на использование для массива вы вкладываете в @table

Так ваша программа должна выглядеть это:

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

Возможно, это то, что вы на самом деле хотите:

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

было бы лучше использовать современную форму open: open (my $ csv, '<', $ ARGV [0]) –

0

Изменить

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

Затем он печатает нормально.

+0

... но делает неправильную вещь '@row 'не создается внутри цикла. –

+0

... но он может быть исправлен, если вы говорите 'push @table, [@row]', чтобы использовать ссылку на копию '@ row'. – mob

2

Если вы вызываете кнопку с списком аргументами, вы добавляете первый список с остающимся списком (ов) в стеке мудрой моде. Читайте о нажатии на Perldoc. Таким образом, ваш звонок push(@table, @row); создает более длинный список @table, а не двухмерный массив.

Вы получили несколько сообщений, которые нажимают ссылку на список @row, так как \@row создаст список строк, и это действительно работает. Я имею тенденцию делать это немного по-другому.Конечно, с Perl всегда есть другой способ сделать это!

Синтаксически вы также можете нажать ссылку на анонимный массив в скалярном элементе списка, чтобы создать список с несколькими измерениями. Самое важное, что нужно знать о ссылках в Perl, это: 1) они являются скалярами и 2) они могут ссылаться на что-либо в Perl-коде, массиве, хэше, другой ссылке. Проведите некоторое время с Perl Ref Tutorial, и это станет более понятным. С вашим кодом просто добавьте [ ] вокруг элемента, который вы хотите быть вторым измерением в своем списке, поэтому push(@table, @row); должен быть push(@table, [ @row ]); В том же смысле вы помещаете [ ] вокруг своего разбиения так, чтобы оно становилось push(@table, [ split(/\s*,\s*/, $_) ]);. Это будет одновременно выполнять разделение и создавать анонимный массив к результату.

Конкретная проблема, с которой вы сталкиваетесь, как создать и получить доступ к многомерному списку, также очень хорошо разбирается в Тома Кристенсена perllol tutorial Решения по вашим конкретным вопросам с вашим кодом напрямую рассматриваются здесь.

Переписывая код с точным кодом из примера Тома в perllol, он становится этим:

#!/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
Смежные вопросы