【问题标题】:Perl - Code EnhancementPerl - 代码增强
【发布时间】:2012-05-25 12:19:49
【问题描述】:

我刚刚开始使用 Perl 进行编码,我只是想看看下面的代码是否可以变得更高效或可以用更少的行数完成。

我已经对Win32::OLE 模块和Text::CSV 模块进行了一些研究,但这似乎与我目前所读的内容不同。

这个问题基本上是一个新手问一个长辈:“嘿,我如何成为一个更好的 Perl 程序员?”

代码的目的是从 Excel 工作簿的指定工作表中的指定范围获取数据,并将这些范围的内容写入 CSV 文件。

另外,我知道我需要执行一般检查,例如确保在将我的 $cellValue 添加到数组之前定义它等,但我更多的是寻找整体结构。有没有办法通过一次将所有整行放入一个数组,或者将整个范围放入一个数组或引用中,或类似的东西来使循环变平?

谢谢

use strict;
use warnings;
use Spreadsheet::XLSX;

my $excel = Spreadsheet::XLSX -> new ('C:\scott.xlsm',);
my @sheets = qw(Fund_Data GL_Data);

foreach my $sheet (@sheets) {

    my $worksheet = $excel->Worksheet($sheet);
    my $cell = $worksheet->get_cell(25,0);

    if ($cell) { # make sure cell value isn't blank
        my $myFile = "C:/$sheet.csv";
        open NEWFILE, ">$myFile" or die $!;

        # write all cells from Range("A25:[MaxColumn][MaxRow]") to a csv file
        my $maxCol = $worksheet->{MaxCol};
        my $maxRow = $worksheet->{MaxRow};
        my @arrRows;
        my $rowString;

        # loop through each row and column in defined range and string together each row and write to file
        foreach my $row (24 .. $maxRow) {

            foreach my $col (0 .. $maxCol) {

                my $cellValue = $worksheet->{Cells} [$row] [$col]->Value();

                if ($rowString) {
                    $rowString = $rowString . "," . $cellValue;
                } else {
                    $rowString = $cellValue;
                }
            }

            print NEWFILE "$rowString\n";
            undef $rowString;
        }
    }
}

【问题讨论】:

  • 顺便说一句,您的代码对于非专家来说已经非常好!您可以采取一些措施来使其更惯用(请参阅答案),但这是一个很好的开始!
  • @DVK +1 表示鼓励。谢谢你。很高兴知道我有一个好的开始。
  • 既然这不是一个真正的问题,恕我直言,它会更适合codereview.stackexchange.com
  • 啊,我不知道 codereview.stackexchange。下次还会去

标签: perl


【解决方案1】:

Mark 的建议非常好。另一个小的改进是将“做一堆嵌套逻辑if $cell”替换为“不要做任何事情unless $cell - 这样你的代码可读性更高(删除1个额外的缩进/嵌套块;并且不要不得不担心如果 $cell 为空会发生什么。

# OLD
foreach my $sheet (@sheets) {
    my $worksheet = $excel->Worksheet($sheet);
    my $cell = $worksheet->get_cell(25,0);

    if ($cell) { # make sure cell value isn't blank
        # All your logic in the if
    }
}

# NEW
foreach my $sheet (@sheets) {
    my $worksheet = $excel->Worksheet($sheet);
    next unless $worksheet->get_cell(25,0); # You don't use $cell, so dropped

    # All your logic that used to be in the if
}

正如您所指出的,Text::CSV 是一件值得考虑的事情,具体取决于您的数据是否需要根据 CSV 标准进行引用(例如,包含空格、逗号、引号等...)。如果可能需要引用,请不要重新发明轮子,而是使用Text::CSV 进行打印。未经测试的例子是这样的:

# At the start of the script:
use Text::CSV;
my $csv = Text::CSV->new ( { } ); # Add error handler!

    # In the loop, when the file handle $fh is opened
    foreach my $row (24 .. $maxRow) {
        my $cols = [ map { $worksheet->{Cells}[$row][$_] } 0 .. $maxCol) ];
        my $status = $csv->print ($fh, $cols);
        # Error handling
    }

【讨论】:

  • 顺便说一句,AFAIR,您可能需要将手动打开的文件句柄替换为 IO::File 对象,以便 Text::CSV 工作
  • 感谢 TEXT::CSV 的帮助。我认为帮助将数据正确写入文件会很方便。现在,我可以看到它是如何完成的,而在我挣扎之前。
【解决方案2】:

没有理由有那个内循环:

print NEWFILE join(",", map { $worksheet->{Cells}[$row][$_] } 0 .. $maxCol), "\n";

另外,请确保您的索引正确无误。我对 Spreadsheet::XLSX 不熟悉,因此请确保 max col & row 与其他代码一样从零开始。如果不是,那么您将需要遍历 0 .. $maxCol-1

【讨论】:

  • 哪个版本的 Perl 正在使用 map?
  • @Mark Mann - 现在太好了!谢谢。
  • @octopusgrabbus - 至少,Perl 4 和更早版本(意思是至少 1991 年以来的任何 Perl)。可能更早,但我是新手,无法使用 Perl 3
  • 谢谢。我正在考虑买一本更新的教科书。
  • @Mark Mann - 另外,感谢基于零的评论。 Spreadsheet::XLSX 确实使用零基数。我发现很难。见stackoverflow.com/questions/10725122/…
【解决方案3】:

我建议不要对文件名进行硬编码...尤其是在像这样的小型项目中,养成通过GetOpt::Long 传递文件名的习惯。如果您对所有小项目都习惯性地这样做,那么当它依赖于一个更大的项目时,更容易记住正确地做。

您的代码结构良好且可读性强,您预见到循环语句的问题,您使用了警告和严格,并且您通常以正确的方式使用库。

【讨论】:

    【解决方案4】:

    正如其他人所说,您的代码清晰且结构良好。但我认为它可以通过多一点 Perlishness 来改进。

    想到以下几点

    • 使用词法文件句柄和open (open my $newfile, '>', $myFile)的三参数形式

    • 迭代哈希或数组值(或它们的切片)而不是它们的键或索引,除非您确实需要循环体的键

      李>
    • 如果是循环的焦点,则提取指向循环内数据子结构的指针 (my $rows = $worksheet->{Cells})

    • 找出您使用循环将一个列表转换为另一个列表的位置,并改用map

    我希望我没有像您建议的那样使用 Text::CSV 编写解决方案。幸运的是,这对你很有启发性。

    use strict;
    use warnings;
    
    use Spreadsheet::XLSX;
    use Text::CSV;
    
    my $csv = Text::CSV->new;
    
    my $excel = Spreadsheet::XLSX->new('C:\scott.xlsm',);
    
    foreach my $sheet (qw/ Fund_Data  GL_Data /) {
    
      my $worksheet = $excel->Worksheet($sheet);
      next unless $worksheet->get_cell(25,0);
    
      my $myFile = "C:\\$sheet.csv";
      open my $newfile, '>', $myFile or die $!;
    
      my $rows = $worksheet->{Cells};
    
      # Write all cells from row 25 onwards to the CSV file
    
      foreach my $row (@{$rows}[24..$#{$rows}]) {
        my @values = map $_ ? $_->Value : '', @$row;
        $csv->print($newfile, \@values);
        print $newfile "\n";
      }
    }
    

    【讨论】:

    • 在这方面做得很好。我真的很喜欢这段代码。有很多答案可供选择......如果可以的话,我也会接受这个作为答案。不完全确定它在底部的 foreach 循环中是如何工作的,但它会提供一些东西来学习!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多