【问题标题】:how to select elements from one array to another with grep in perl?如何在perl中使用grep从一个数组中选择元素到另一个数组?
【发布时间】:2020-11-23 11:17:22
【问题描述】:

我想捕捉CWD 中的文件和@files 中的文件之间的区别:

#!/usr/bin/perl -w
use Cwd qw[getcwd abs_path];
opendir CWD, getcwd;
@files=grep{!/^\./}readdir CWD;

push @files, ("foo.txt", "bar.txt");

for my $i (@files){
    @difference=grep { !/^\./ and $i!=$_ } readdir CWD;
}
print "$_\n" for @differenc 

现在当前目录有以下文件:

$ls
a.txt  e.txt  getopt.html

用这个表达式push @files, ("foo.txt", "bar.txt"); 该数组具有以下元素:("foo.txt", "bar.txt", "a.txt", "e.txt", "getopt.html"),这是正确的,但现在我只想选择那些在 CWD 中 not 的文件: @difference=grep { !/^\./ and $i!=$_ } readdir CWD;,所以我希望数组@difference 再次只有("foo.txt", "bar.txt")(我现在这没有意义,这是一个例子)。但是print什么都没有输出,怎么回事?

【问题讨论】:

  • 正如您自己编写的那样,您的示例没有多大意义,这让我想知道这是否不是XY problem。考虑解释您要达到的目标,我们也许可以提供更好的答案。

标签: arrays perl grep text-processing


【解决方案1】:

您在同一个文件句柄上有两个readdir 操作。一旦第一个到达 EOF,就不会再返回任何内容了。

在第二次迭代之前添加一个`rewinddir 操作。

【讨论】:

    【解决方案2】:

    你的代码有很多问题:

    • 您的第二个readdir(在for my $i (@files) 循环中)没有读取任何内容,因为第一个(@files=grep{!/^\./}readdir CWD;)已经读取了整个目录。您可以先使用rewinddir,但只需使用@files 的副本(在推送foo.txtbar.txt 之前)会更简单、更高效。

    • 您正在使用!= 而不是ne 来比较字符串。

    • 循环的每次迭代都会删除@differences 的前一个值,因为您将它分配给=。大概push 会更有意义。

    • 循环内部的逻辑有点缺陷。 grep 返回满足条件的元素,但你更感兴趣的是有没有满足条件的元素。

    • 您应该检查opendir 是否成功(通过将or die ... 添加到opendir 行)。另外请注意,open my $CWD, getcwd 等价于更简单的open my $CWD, "."

    您可能想要做的是:

    use strict;
    use warnings;
    
    opendir my $CWD, "." or die "Could not open '.': $!";
    
    my @files = grep{!/^\./} readdir $CWD;
    my @init_files = @files;
    
    push @files, ("foo.txt", "bar.txt");
    
    my @difference;
    for my $i (@files){
        push @difference, (grep { !/^\./ and $i eq $_ } @init_files) ? () : $i;
    }
    print "$_\n" for @difference
    

    然而,这远非高效,而且需要更加复杂。我建议改为:

    my %files = map { $_ => 1 } grep {!/^\./} readdir $CWD;
    
    my @difference;
    for ("foo.txt", "bar.txt") {
        push @difference, $_ if ! exists $files{$_};
    }
    print "$_\n" for @difference
    

    请注意,我已将 use strict; use warnings; 添加到脚本中。始终将它们添加到您的代码中。虽然在这种特定情况下,它不会帮助您找出问题所在,但它会在未来为您节省无数小时。此外,始终使用词法文件/目录句柄(即使用opendir my $CWD, "dir" 而不是open CWD, "dir")。

    【讨论】:

    • 我只对变量使用$,对句柄使用大写。它使我的代码更干净,为什么要混淆它们?
    • @milanHrabos 它不会让你的代码更干净。首先,在前面添加$ 无需任何费用。其次,如果你在使用strict$ 时出现拼写错误(比如CDW 而不是CWD),Perl 将在编译时发出错误。没有$,这条线最终会失败,也许会默默地失败,你将很难调试它。第三,使用my $ 意味着您的变量是词法范围的,这意味着您在调用函数时不会有任何问题。
    • @milanHrabos 例如,考虑sub f { open FH, "<", "f.txt"; $l1 = <FH>; g(); @rest = <FH> }sub g { open FH, "<", "g.txt"; print for <FH> }。好吧,f 没有像您期望的那样工作,因为g 正在使用与f 相同的变量打开一个文件,因此关闭了f 打开的文件。如果您改用my $FH,这将不是问题。
    • @milanHrabos 应该限制变量范围的想法(例如,通过避免全局变量 (CWD) 支持适当范围的变量 (my $CWD))是计算机科学的基本原则
    猜你喜欢
    • 2017-02-08
    • 2012-06-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-06
    • 1970-01-01
    • 2013-11-02
    相关资源
    最近更新 更多