代码之家  ›  专栏  ›  技术社区  ›  aks

我是否应该手动设置Perl的@ARGV以便使用<>打开、扫描和关闭文件?

  •  5
  • aks  · 技术社区  · 15 年前

    readdir() 我已经设法用所有可搜索的文件名构建了一个数组,现在需要搜索每个文件中的模式,我的实现如下所示-

    sub searchDir($) {
        my $dirN = shift;
        my @dirList = glob("$dirN/*");
        for(@dirList) {
            push @fileList, $_ if -f $_;
    
        }
        @ARGV = @fileList;
        while(<>) {
            ## Search for pattern
        }
    }
    

    我的问题是-可以像上面那样手动加载@ARGV数组并使用<&燃气轮机;操作员要扫描单独的行,还是我应该单独打开/扫描/关闭每个文件?如果此处理存在于子例程中而不存在于主函数中,会有什么不同吗?

    5 回复  |  直到 15 年前
        1
  •  9
  •   James Thompson    15 年前

    关于操纵@ARGV的主题——这绝对是有效的代码,Perl当然允许您这样做。但我认为这不是一个好的编码习惯。我所看到的大多数使用“while(<)”习惯用法的代码都是使用它来读取标准输入,而这正是我最初希望您的代码能够做到的。更具可读性的模式可能是单独打开/关闭每个输入文件:

    foreach my $file (@files) {
        open FILE, "<$file" or die "Error opening file $file ($!)";
        my @lines = <FILE>;
        close FILE or die $!;
    
        foreach my $line (@file) {
            if ( $line =~ /$pattern/ ) {
                # do something here!
            }
        }
    }
    

    这对我来说会更容易理解,尽管它只需要几行代码。Perl为您提供了很大的灵活性,但我认为这使得用Perl开发自己的风格变得更加重要,这种风格对您(以及您的同事,如果这对您的代码/职业生涯很重要的话)来说是可读和可理解的。

    将子例程放在主函数或子例程中也是一个风格上的决定,您应该仔细考虑。现代计算机在这方面的速度如此之快,以至于样式和可读性对于这样的脚本来说更为重要,因为您不太可能遇到这样的情况,即这样的脚本会对您的硬件造成过大的负担。

    祝你好运Perl很有趣。:)

    while ( my $line = <FILE> ) {
        if ( $line =~ /$pattern/ ) {
            # do something here!
        }
    }
    

    当我写“你不太可能遇到这样一个脚本对你的硬件征税过多的情况”的时候,我的意思就是要涵盖这一点,抱歉没有说得更具体。此外,谁还有4GB硬盘,更不用说4GB了 ? :P

    另一个编辑:在阅读了互联网上的评论后,我意识到有比4GB大得多的硬盘可供购买。我感谢评论者指出这一点,并承诺今后永远不会- 曾经

        2
  •  3
  •   Frank    15 年前

    我更喜欢这个更清晰易懂的版本:

    #!/usr/bin/perl -w 
    
    foreach my $file (<$ARGV[0]/*>){
        open(F, $file) or die "$!: $file";
        while(<F>){
          # search for pattern
        }
        close F;
    }
    

    但操纵也可以 @ARGV

    #!/usr/bin/perl -w 
    
    @ARGV = <$ARGV[0]/*>;
    while(<>){
        # search for pattern
    }
    
        3
  •  1
  •   Jonathan Leffler Toon Krijthe    15 年前

    是的,可以在启动' while (<>) "环",;在循环中调整它几乎是鲁莽的。例如,如果处理选项参数,通常会从@ARGV;在这里,您正在添加项目,但它仍然会更改@ARGV的原始值。

    无论代码是在子例程中还是在“主函数”中,都没有任何区别。

        4
  •  1
  •   lexu    15 年前

    前面的答案很好地涵盖了主要的Perl编程问题。

    比如说,根据操作系统的不同,调用专门的外部程序可能是有意义的

    grep -l <pattern> <path>
    

    在unix上。

    根据您需要对包含模式的文件执行什么操作,以及命中/未命中率有多大,这可能会节省大量时间(并重新使用经过验证的代码)。

        5
  •  0
  •   daotoad    15 年前

    while (<>) special magic attributes . (正在读取中的每个文件) @ARGV STDIN 如果 @ARGV 是空的,测试的是确定性而不是真实性)。为了减少需要理解的魔法,我会避免它,除了快速黑客工作。

    您可以通过选中来获取当前文件的文件名 $ARGV .

    您可能没有意识到这一点,但实际上您正在影响两个全局变量,而不仅仅是 @ARGV . 你也在打 $_ . 本地化是一个非常非常好的主意

    您可以使用 local 将更改本地化。

    顺便说一句,还有另一个重要的,微妙的魔术 <> $. 给出最后访问的句柄中的行号——很好。但这里潜藏着一个问题-- $. @ARGV eof

    use strict;
    use warnings;
    
    ...
    
    searchDir( 'foo' );
    
    sub searchDir {
        my $dirN    = shift;
        my $pattern = shift;
    
        local $_;
    
        my @fileList = grep { -f $_ } glob("$dirN/*");
    
        return unless @fileList;  # Don't want to process STDIN.
    
        local @ARGV;
    
        @ARGV = @fileList;
        while(<>) {
            my $found = 0;
            ## Search for pattern
            if ( $found ) {
                print "Match at $. in $ARGV\n";
            }
        }
        continue {
            # reset line numbering after each file.
            close ARGV  if eof;  # don't use eof().
        }
    }
    

    警告

    使现代化 :使用的原因 地方的 而不是 my 他们做的事情非常不同。 创建一个新的 词汇变量 仅在包含的块中可见,无法通过符号表访问。 地方的 包变量 并将其别名为新变量。新的本地化版本在任何后续代码中都是可见的,直到我们离开封闭块为止。看见 perlsub: Temporary Values Via local()

    在创建新变量并使用它们的一般情况下, 这是正确的选择。 地方的

    此简短脚本演示了本地:

    $foo = 'foo';
    
    print_foo();
    print_bar();
    print_foo();
    
    sub print_bar {
        local $foo;
        $foo = 'bar';
        print_foo();
    }
    
    sub print_foo {
        print "Foo: $foo\n";
    }