Perl - 代码增强

时间:2012-05-24 20:19:48

标签: perl

我刚刚开始使用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; } } } 等等,但我正在寻找更多的整体结构。就像有没有办法通过将所有整行一次放入一个数组,或者数组或引用中的整个范围,或者那种性质的东西来展平循环?

由于

{{1}}

4 个答案:

答案 0 :(得分:6)

没有理由拥有内循环:

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

另外,请确保您的索引正确无误。我不熟悉Spreadsheet :: XLSX,所以请确保max col& row与其他代码一样为零。如果他们不是那么你会想要迭代0 .. $maxCol-1

答案 1 :(得分:6)

马克的建议是一个很好的建议。另一个小的改进是替换“做一堆嵌套逻辑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
    }

答案 2 :(得分:4)

我建议不要硬编码文件名...特别是在像这样的小项目中,养成在via GetOpt::Long中传递文件名的习惯。如果你习惯性地使用所有的小项目,那么当它依赖于一个更大的项目时,它就会更容易记住这样做。

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

答案 3 :(得分:4)

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

我想到以下几点

  • 使用词法文件句柄和openopen my $newfile, '>', $myFile

  • 的三参数格式
  • 迭代哈希值或数组值(或 slice )而不是它们的键或索引,除非你真的需要循环体的键

  • 如果这是循环的焦点(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";
  }
}