Perl sub跳过调用它的foreach

时间:2017-04-05 07:16:09

标签: perl

我遇到了一个子程序的问题,该子程序找到某些文件并从中提取一些数据。

在foreach循环中调用此子例程,但无论何时调用,循环都会跳到下一次迭代。所以我想知道下一个是否会以某种方式从子例程转移到foreach循环中调用它?

据我所知,该子看起来很稳固所以我希望有人能看到我失踪的东西吗?

sub FindKit{
    opendir(DH, "$FindBin::Bin\\data");
    my @kitfiles = readdir(DH);
    closedir(DH);

    my $nametosearch = $_[0];
    my $numr = 1;
    foreach my $kitfile (@kitfiles)
    {
        # skip . and .. and Thumbs.db and non-K-files
        if($kitfile =~ /^\.$/) {shift @kitfiles; next;}
        if($kitfile =~ /^\.\.$/) {shift @kitfiles; next;}
        if($kitfile =~ /Thumbs\.db/) {shift @kitfiles; next;}
        if($kitfile =~ /^[^K]/) {shift @kitfiles; next;}

        # $kitfile is the file used on this iteration of the loop
        open (my $fhkits,"<","data\\$kitfile") or die "$!";
        while (<$fhkits>) {}
        if ($. <= 1) {
            print " Empty File!";
            next;
        }
        seek($fhkits,0,0);
        while (my $kitrow = <$fhkits>) {
            if ($. == 0 && $kitrow =~ /Maakartikel :\s*(\S+)\s+Montagekit.*?($nametosearch)\s{3,}/g) {
                close $fhkits;
                return $1;
            }
        }
        $numr++;
        close $fhkits;
    }
    return 0;
}

3 个答案:

答案 0 :(得分:1)

总结评论,重构代码:

use File::Glob ':bsd_glob';

sub FindKit {
    my $nametosearch = $_[0];

    my @kitfiles = glob "$FindBin::Bin/data/K*";  # files that start with K
    foreach my $kitfile (@kitfiles)
    {
        open my $fhkits, '<', $kitfile or die "$!";

        my $kitrow_first_line = <$fhkits>;    

        1 while <$fhkits>;    # check number of lines ...

        return if $. == 1;    # there was only one line, the header

        my ($result) = $kitrow_first_line =~ 
            /Maakartikel :\s*(\S+)\s+Montagekit.*?($nametosearch)\s{3,}/;

        return $result if $result;
    }
    return 0;
}

我使用核心File::Glob并启用:bsd_glob选项,它可以处理文件名中的空格。我按照文档说明在Win32系统上使用“ real slash ”。

除了返回值之外,我没有看到它如何影响调用代码。此外,我也看不到发布的代码如何使调用者跳过节拍。这个问题不太可能出现在这个问题上。

如果我错过了上述重写的一些内容,请告诉我。

答案 1 :(得分:1)

这几乎肯定会让你感到困惑的是,你正在shift你正在迭代的列表。

这是个坏消息,因为你正在删除元素......但是在某些地方你并不一定在思考。

例如:

#!/usr/bin/env perl

use strict;
use warnings;

my @list = qw ( one two three ); 
my $count;

foreach my $value ( @list ) {
   print "Iteration ", ++$count," value is $value\n";
   if ( $value eq 'two' ) { shift @list; next };
}

print "@list";

您认为应该迭代多少次,哪些值最终应该在数组中?

因为你shift你永远不会处理元素&#39;三&#39;并删除元素&#39; one&#39;。这几乎可以肯定是什么导致了你的问题。

您还:

  • open使用相对路径,当opendir使用绝对路径时。
  • 跳过一堆文件,然后跳过任何不以K开头的内容。为什么不只搜索K开头的内容?
  • 读取文件两次,一个是检查它是否为空。 perl file test -z会做得很好。
  • 您为文件中的每一行设置了$kitrow,但并不是真的将它用于模式匹配以外的任何内容。它可能使用隐式变量更好地工作。
  • 您实际上只在第一行执行任何操作 - 因此您无需迭代整个文件。 ($numr似乎被丢弃了)。
  • 您使用全局匹配,但只使用一个结果。这里g标志似乎多余。

我建议进行重大改写,并执行以下操作:

#!/usr/bin/env perl

use strict;
use warnings;
use FindBin;

sub FindKit{
    my ($nametosearch) = @_;

    my $numr = 1;
    foreach my $kitfile (glob "$FindBin::Bin\\data\\K*" )
    {
       if ( -z $kitfile ) {
           print "$kitfile is empty\n";
           next;
        }

        # $kitfile is the file used on this iteration of the loop
        open (my $fhkits,"<", $kitfile) or die "$!";
        <$kitfile> =~ m/Maakartikel :\s*(\S+)\s+Montagekit.*?($nametosearch)\s{3,}/ 
             and return $1; 
        return 0; 
    }
}

答案 2 :(得分:1)

作为Path::Tiny模块的忠实粉丝(我总是在每个项目中安装并使用它),我的解决方案是:

groups = [[],['A','B']]

一些评论仍未解决问题:

  • 使用Path::Tiny,无论操作系统(UNIX / Windows)如何,都可以在路径名中使用正斜杠,例如>>> {k:head for head, *tail in grps for k in tail} Traceback (most recent call last): File "<stdin>", line 1, in <module> File "<stdin>", line 1, in <dictcomp> ValueError: not enough values to unpack (expected at least 1, got 0) 也适用于Windows。
  • FindBin的AFAIK为considered broken - 因此以上内容使用use strict; use warnings; use Path::Tiny; my $found = FindKit('mykit'); print "$found\n"; sub FindKit { my($nametosearch) = @_; my $datadir = path($0)->realpath->parent->child('data'); die "$datadir doesn't exists" unless -d $datadir; for my $file ($datadir->children( qr /^K/ )) { next if -z $file; #skip empty my @lines = $file->lines; return $1 if $lines[0] =~ /Maakartikel :\s*(\S+)\s+Montagekit.*?($nametosearch)\s{3,}/; } return; } data/file ...
  • 如果套件包含多个文件怎么办?以上总是在第一个找到的
  • 上返回
  • $0读取所有行 - 不必要 - 但是对于小文件并不重要。
  • 现实这个函数返回realpath的arg,所以可能更好的名称是my @lines = $file->lines;Maakartikel:)
  • 轻松切换为find_articel_by_kit - 只需将find_articel更改为utf8