Perl - 代码审查

时间:2013-03-05 14:12:40

标签: perl file text counting

我正在开发一个程序,该程序将CSV文件中的信息作为源来搜索,并通过具有“客户包”的文本文件进行搜索。我只对一些条目感到奇怪,我似乎无法弄清楚导致重复计数的原因。任何人都可以查看我的代码并告诉我我的逻辑/语法是否关闭? (可能是)。我想要完成的是计算csv文件(packageid,package_description)中条目的文本文件中的总出现次数

感谢您的帮助!我在这里疯狂。

#!/usr/bin/perl

use strict;
use Text::CSV;

# Variables already declared in the other PL file ** Remove if consolidating **

my $file2 = 'master_plist.csv';
my $csv2 = Text::CSV->new(); # Create a Text::CSV object

open (CSV2, "<", $file2) or die $!; #open CSV file for parsing

while (<CSV2>) {

    if ($csv2->parse($_)) {
            my @columns2 = $csv2->fields(); # Parse CSV and load into an array for each row.
            my $packID = $columns2[0];
            my $packDESC = $columns2[1];



my $val = 'customer_packages_report.txt';

chomp ($val);

my $cnt=0;

open (HNDL, "$val") || die "wrong filename";

while ($val = <HNDL>)
{
while ($val =~ /$packID - $packDESC/ig)
{
    $cnt++;
}
}

#if ($packDESC =~ /\(/g) {
#       $packDESC =~ s/\(/\(/g;
#} 
print "Total iterations of $packDESC: $cnt\n";

close (HNDL);
# End original code

    } # Close IF
} # Close WHILE

close CSV;

5 个答案:

答案 0 :(得分:2)

#!/usr/bin/perl

use strict;
use warnings;
use Text::CSV;

# Variables already declared in the other PL file ** Remove if consolidating **

my $file2 = 'master_plist.csv';
my $csv2 = Text::CSV->new(); # Create a Text::CSV object

open (CSV2, "<", $file2) or die "I die while opening $file2!  $!"; #open CSV file for parsing

while ($each_csv2_line=<CSV2>) {

    if ($csv2->parse($each_csv2_line)) {
            my @columns2 = $csv2->fields(); # Parse CSV and load into an array for each row.
            my $packID = $columns2[0];
            my $packDESC = $columns2[1];



            my $val = 'customer_packages_report.txt';

            chomp ($val);

            my $cnt=0;

            open (HNDL,"<","$val") or die "wrong filename: $val! $!";

            while (<HNDL>){
                $cnt++ while (/$packID - $packDESC/ig);
            }

#if ($packDESC =~ /\(/g) {
#       $packDESC =~ s/\(/\(/g;
#} 
            print "Total iterations of $packDESC: $cnt\n";

            close (HNDL);
            # End original code

    } # Close IF
} # Close WHILE

# end of script
close CSV;

我的建议:

  • 更好地使用$HNDL instead of HNDL&lt; - 文件句柄的词法变量。
  • 尝试捕捉所有错误(defined==0eq ""
  • 我尝试格式化您的代码并添加一些我有时会使用的功能。比我好,先阅读Style Coding for Little Perl Monk。使用这种语言你会更加令人印象深刻,不仅可以编写writeonly代码。

示例(以及引用):

“线路输入操作员<>的情况完全相同,尽管Perl会自动为您执行此操作。
看起来你正在测试STDIN中的这一行:

    while (<STDIN>) {
       do_something($_);
    }

但是,这是一个特殊情况,Perl会自动转换为检查$_的定义:

     while ( defined( $_ = <STDIN> ) ) {  # implicitly done
       do_something($_);
     }

” 有效的Perl编程,第24页。

答案 1 :(得分:2)

您可以做很多事情来改进代码:

  1. use warnings;
  2. 使用适当的缩进。
  3. 使用描述性变量名称。而不是$file2(没有意义,为什么没有文件1?),请使用$package_file或任何有意义的内容。
  4. 如果您已经在使用Text::CSV,则可以使用$csv->getline()逐行浏览文件。这将简化您的代码。 See the documentation for an example
  5. chomp($val)从字符串末尾删除换行符。您在刚刚声明的字符串文字中使用它,它没有换行符。这没有意义。
  6. 从不使用相同的变量($val)来完成两件完全不同的事情。这非常令人困惑。
  7. 您在正则表达式中插入的变量可能包含特殊字符吗?如果是这样,你需要逃脱它们。例如,如果$packDESC包含句点,则它将匹配正则表达式中的任何字符。要按字面意思处理变量的内容,请使用\Q..\E,如下例所示:/\Q$packID - $packDESC\E/ig

  8. 您正在打开customer_packages_report.txt并在csv文件的每一行上逐行浏览。你可以通过在中读取并将结果存储在一个数组中来简化它。

  9. 您不需要使用while循环来计算匹配项:$cnt = () = /$packID - $packDESC/ig;。这将匹配放在数组上下文中,返回一个匹配数组,然后将其放回标量上下文中以计算匹配。有点棘手,但更简单。
  10. 如果没有看到数据,很难确切地说出导致问题的原因。您是否有一些不必要的重复,这些重复源于您对两个文件的嵌套循环?我将从重写改进代码开始,然后查看问题是否仍然存在。

答案 2 :(得分:1)

您的代码似乎使用perl -c编译而没有错误,所以这很好。如果我猜,我会认为你的问题在于你的某些领域有元字符。正则表达式/$packID - $packDESC/容易受到元字符的影响。例如

my $str = "foo? bar";
$str =~ /$str/;       # returns false, because ? is a meta character

在上面的示例中,问号?是一个量词,它影响到它之前的任何内容,因此o?表示“0或1 o”。要解决元字符问题,请使用\Q ... \E转义符:

$str =~ /\Q$str/;   # will now match

使用\E终止转义序列是可选的。


其他一些注意事项:

  • 使用use strict非常好。您还应始终use warnings。不这样做不是删除代码中的问题,只是隐藏它们。
  • 使用默认设置创建Text::CSV对象。根据您的输入,可能适合也可能不适合。在the documentation
  • 中建议设置binary => 1
  • 使用parse()函数可能不是最佳选择,文档对getline有好话要说。
  • 正如loldop在评论中指出的那样,您正在重复使用$val来读取您的文件。虽然从技术上说应该工作,但它却在寻找麻烦。

风格和练习笔记及实用技巧:

  • 使用三参数open和lexical文件句柄是一件好事。三个参数本质上意味着使用显式打开模式,这使您的脚本更安全。使用词法文件句柄意味着你的文件句柄不会有全局范围,这是一件好事。
  • 此代码

my @columns2 = $csv2->fields(); 
my $packID = $columns2[0];
my $packDESC = $columns2[1];

可以这样写

my ($packID, $packDESC) = $csv2->fields();
  • 分配后,您正在扼杀$val。这是多余的,因为chomp默认情况下仅从字符串末尾删除换行符,并且您没有添加任何此类换行符。它不会改变任何东西,但这里不需要。如果您从stdin或文件中读取内容,则可能需要使用chomp
  • 使用die而不提及错误$!是让自己烦恼的可靠方法。
  • 不要低估使用适当的缩进时编写代码的容易程度。使用具有自动缩进和着色的文本编辑器。我可以热烈推荐vim(如果你使用的话是gvim)。虽然它有一个学习曲线,但它是一个功能强大的编辑器,通常也已安装在许多系统上。

答案 3 :(得分:1)

由于很多人已经对您的程序本身进行过评论,我将谈谈如何成为一名更好的Perl程序员,并帮助以有助于消除您的许多问题的方式编写。

查看Perl::Tidy并完成您的程序。这将有助于改善您的语法和Perl,并将帮助您解决许多您遇到的各种问题。

另外,你应该得到Perl Best Practices的副本,这是Perl Tidy的大部分内容。并且,正如已经引用Effective Perl Programming的人是另一本优秀的书。

Perl的一大问题是很少有人学习它。大多数人都陷入了我们不得不自己捡起来的情况。另外,Perl是一种相当陈旧且相当苛刻的语言。大多数Perl书籍仍然严重依赖于Perl 3.x编程方式,并且没有提到使用use strict;use warnings;这样的基础知识。

你结合了旧的编程实践,大多数人通过使用旧语法破解老式程序来学习Perl(可能是通过黑客攻击甚至更旧的程序来学习Perl的人),你可以看到为什么Perl有作为只写语言的声誉。

答案 4 :(得分:0)

您可能希望使用getline中的Text::CSV方法,该方法可以保存几行代码。

问题可能是因为您在搜索的字符串中有正则表达式元字符。在正则表达式中使用\Q...\E转义它们,以便按字面意思取消它们。在下面的重写中,我还添加了\s*而不是文字空格,以防连字符两边没有一个空格。

我还将文件句柄更改为词法句柄,这样做的好处是当句柄超出范围时它们会自动关闭。

#!/usr/bin/perl

use strict;
use warnings;

use Text::CSV;

my $file2 = 'master_plist.csv';
my $csv2  = Text::CSV->new();

open(my $csv_fh, '<', $file2) or die $!;

while (my $row = $csv2->getline($csv_fh)) {

  my ($packID, $packDESC) = @$row;

  my $val = 'customer_packages_report.txt';
  chomp($val);

  open(my $fh, '<', $val) or die "wrong filename";
  my $cnt = 0;
  while ($val = <$fh>) {
    while ($val =~ /\Q$packID\E\s*-\s*\Q$packDESC\E/ig) {
      $cnt++;
    }
  }

  print "Total iterations of $packDESC: $cnt\n";
}