【问题标题】:How can I print a Perl two-dimensional array?如何打印 Perl 二维数组?
【发布时间】:2011-03-02 09:58:41
【问题描述】:

我正在尝试编写一个简单的 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";

当我运行此脚本时,我收到以下错误并且没有打印任何内容:

在 ./scripts.pl 第 16 行使用“strict refs”时,不能使用字符串 ("1") 作为 ARRAY ref。

我查看了许多其他论坛,但仍然不确定如何解决此问题。我该如何解决这个问题?

【问题讨论】:

标签: perl arrays


【解决方案1】:

您不是在创建二维数组(Perl 术语中的 AoA 或“数组数组”)。这一行:

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

【讨论】:

  • Array-of-array 是正确的命名法。在 Perl 中确实没有列表列表这样的东西。 (尽管有 perllol 联机帮助页的名称。叹息。)
  • @friedo:点了。我见过多次使用 LoL,但这不是传播误解的借口。固定。
【解决方案2】:
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

【讨论】:

    【解决方案3】:

    如果您使用列表参数调用 push,则以堆栈方式将第一个列表与剩余列表附加在一起。在Perldoc 阅读有关推送的信息。所以你对push(@table, @row); 的调用是在创建一个更长的@table 列表,而不是一个二维数组。

    您收到了几篇帖子,将@row 的列表引用推送为\@row 将创建一个行列表,这确实有效。我倾向于做一些不同的事情。当然,对于 Perl,总有另一种方法可以做到这一点!

    在语法上,您还可以将匿名数组引用推送到列表的标量元素中以创建多维列表。关于 Perl 中的引用,最重要的一点是:1) 它们是标量,2) 它们可以引用 Perl 中的任何东西——代码、数组、散列、另一个引用。花一些时间使用Perl Ref Tutorial,这将变得更加清晰。使用您的代码,只需在您希望成为列表中第二维的元素周围添加[ ],因此push(@table, @row); 应该是push(@table, [ @row ]); 在同样的意义上,您将[ ] 放在您的拆分周围,使其变为@ 987654332@ 这将同时执行拆分并为结果创建一个匿名数组。

    您遇到的具体问题,如何创建和访问多维列表,在 Tom Christensen 的perllol tutorial 中也得到了很好的处理,您的代码的具体问题的解决方案在这里直接处理。

    使用 Tom 在 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
    

    【讨论】:

      【解决方案4】:

      你需要做两处改变:

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

      【讨论】:

        【解决方案5】:

        也许这就是你真正想要的:

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

        【讨论】:

        • 最好使用现代形式的 open: open(my $csv, '
        【解决方案6】:

        改变

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

        然后就可以打印出来了。

        【讨论】:

        • ...但是@row 的错误行为不是在循环内创建的。
        • ...但如果您说push @table, [@row] 使用对@row 副本的引用,则可以修复它。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-31
        • 1970-01-01
        • 2020-11-17
        • 2021-12-11
        相关资源
        最近更新 更多