Как распечатать 2-мерный массив Perl?

Я пытаюсь написать простой скрипт на Perl, который читает *.csv, размещает строки *.csv-файл в двумерном массиве, а затем печатает на элементе из массива, а затем печатает строку массива.

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

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

Не может использовать string ("1") в качестве ссылки на массив, в то время как" strict refs " используется at ./scripts.pl строка 16.

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

6 ответов


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

push(@table, @row);

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

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

при использовании split хорошо для тривиальных CSV-файлов, это прискорбно неадекватно для чего-либо еще. Используйте модуль как текст::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";

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 изменения:

  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";       

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

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

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

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

переписывая ваш код с точным кодом из примера Тома в 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,00123

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

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

изменить

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

затем он печатает ok.