在Perl中,有什么理由更喜欢glob而不是readdir(反之亦然)?

时间:2009-10-01 22:15:12

标签: perl glob readdir

这个问题是this one的衍生产品。一些历史:当我第一次学习Perl时,我几乎总是使用glob而不是opendir + readdir因为我发现它更容易。后来各种帖子和阅读材料都表明glob很糟糕,所以现在我几乎总是使用readdir

在思考this recent question之后,我意识到我选择其中一个或另一个选择的原因可能是无聊的。所以,我将列出一些优点和缺点,我希望更有经验的Perl人可以插入并澄清。简而言之,问题是否有令人信服的理由选择globreaddirreaddirglob(在某些或所有情况下)?

glob专业人士:

  1. 没有dotfiles(除非你要求)
  2. 保证物品的顺序
  3. 无需手动将目录名称添加到项目
  4. 更好的名字(来吧 - globreaddir不是竞争,如果我们只是通过名字判断的话)
  5. (来自ysth的回答;参见下面的glob缺点4)可以返回不存在的文件名:

    @deck = glob "{A,K,Q,J,10,9,8,7,6,5,4,3,2}{\x{2660},\x{2665},\x{2666},\x{2663}}";
    
  6. glob缺点:

    1. 旧版本只是简单破解(但是“较旧”意味着5.6之前,我认为,坦率地说,如果你使用的是5.6之前的版本,你会遇到更大的问题)
    2. 每次都会调用stat(例如,在大多数情况下无用stat)。
    3. 目录名称中的空格问题(这仍然是真的吗?)
    4. (来自brian的回答)可以返回不存在的文件名:

      $ perl -le 'print glob "{ab}{cd}"'
      
    5. readdir专业人士:

      1. (来自brian的回答)opendir返回一个文件句柄,您可以在程序中传递(并重复使用),但glob只返回一个列表
      2. (来自brian的回答)readdir是一个正确的迭代器,并为rewinddirseekdirtelldir提供函数
      3. 更快? (根据上面glob的一些特征进行纯粹的猜测。无论如何,我并不是真的担心这种优化水平,但它是理论专家。)
      4. glob
      5. 更不容易出现边缘案例错误
      6. 默认情况下读取所有内容(dotfiles)(这也是con)
      7. 可能会说服你不要命名文件0(也是骗子 - 见Brad的回答)
      8. 任何? Bueller? Bueller?
      9. readdir缺点:

        1. 如果您不记得预先添加目录名称,那么当您尝试进行文件测试或复制项目或编辑项目或<... li>时,获取位
        2. 如果您不记得grep ...项,{strong>将在您计算物品或尝试行走时获取位以递归方式向下移动文件树或...
        3. 我是否提到过预先填写目录名称? (旁注,但我在Perl初学者邮件列表中的第一篇文章是经典的,“为什么这个涉及文件测试的代码在某些时候不起作用?”与此问题有关的问题。显然,我仍然很痛苦。)< / LI>
        4. 不按特定顺序退回商品。这意味着您经常需要记住以某种方式对它们进行排序。 (如果它意味着更快的速度,这可能是专业人士,如果这意味着你实际上认为关于如何以及是否需要对项目进行排序。)编辑:可疑的小样本,但在Mac readdir按字母顺序返回项目,不区分大小写。在Debian盒子和OpenBSD服务器上,订单是完全随机的。我用Apple的内置Perl(5.8.8)和我自己编译的5.10.1测试了Mac。 Debian框是5.10.0,OpenBSD机器也是如此。我想知道这是文件系统问题,而不是Perl?
        5. 默认情况下读取所有内容(dotfiles)(这也是专业版)
        6. 不一定能很好地处理名为0的文件(参见专业人士 - 请参阅Brad的回答)

10 个答案:

答案 0 :(得分:43)

您错过了它们之间最重要,最大的区别:glob会返回一个列表,但opendir会为您提供目录句柄。您可以传递该目录句柄,让其他对象或子例程使用它。使用目录句柄,子例程或对象不必知道它来自何处,还有谁在使用它,等等:

 sub use_any_dir_handle {
      my( $dh ) = @_;
      rewinddir $dh;
      ...do some filtering...
      return \@files;
      }

使用dirhandle,你有一个可控制的迭代器,你可以使用seekdir移动,虽然使用glob你只需要下一个项目。

与任何事情一样,成本和收益仅在适用于特定情境时才有意义。它们不存在于特定用途之外。你有一个很好的差异列表,但我不会在不知道你试图用它们做什么的情况下对这些差异进行分类。

要记住的其他一些事情:

  • 您可以使用opendir实现自己的glob,但不是相反。

  • glob使用自己的通配符语法,这就是你得到的。

  • glob可以返回不存在的文件名:

    $ perl -le 'print glob "{ab}{cd}"'
    

答案 1 :(得分:8)

glob pros:可以返回不存在的'filenames':

my @deck = List::Util::shuffle glob "{A,K,Q,J,10,9,8,7,6,5,4,3,2}{\x{2660},\x{2665},\x{2666},\x{2663}}";
while (my @hand = splice @deck,0,13) {
    say join ",", @hand;
}
__END__
6♥,8♠,7♠,Q♠,K♣,Q♦,A♣,3♦,6♦,5♥,10♣,Q♣,2♠
2♥,2♣,K♥,A♥,8♦,6♠,8♣,10♠,10♥,5♣,3♥,Q♥,K♦
5♠,5♦,J♣,J♥,J♦,9♠,2♦,8♥,9♣,4♥,10♦,6♣,3♠
3♣,A♦,K♠,4♦,7♣,4♣,A♠,4♠,7♥,J♠,9♥,7♦,9♦

答案 2 :(得分:6)

这是opendirreaddir的缺点。

{
  open my $file, '>', 0;
  print {$file} 'Breaks while( readdir ){ ... }'
}
opendir my $dir, '.';

my $a = 0;
++$a for readdir $dir;
print $a, "\n";

rewinddir $dir;

my $b = 0;
++$b while readdir $dir;
print $b, "\n";

您可能希望代码两次打印相同的数字,但它不会,因为有一个名为0的文件。在我的计算机上打印251188,使用Perl v5.10.0和v5.10.1进行测试

这个问题也使得它只打印出一堆空行,而不管文件0是否存在:

use 5.10.0;
opendir my $dir, '.';

say while readdir $dir;

这在哪里总能正常运作:

use 5.10.0;
my $a = 0;
++$a for glob '*';
say $a;

my $b = 0;
++$b while glob '*';
say $b;

say for glob '*';
say while glob '*';

我修复了这些问题,并发送了一个补丁程序,使其成为Perl v5.11.2,因此当它出现时,这将适用于Perl v5.12.0。

我的修正转换了这个:

while( readdir $dir ){ ... }

进入这个:

while( defined( $_ = readdir $dir ){ ...}

这使得它的工作方式与read处理文件的方式相同。实际上它是相同的代码,我只是在相应的if语句中添加了另一个元素。

答案 3 :(得分:5)

glob可以方便地读取给定固定深度的所有子目录,如glob "*/*/*"中所示。我在几个场合都发现了这个方便。

答案 4 :(得分:4)

嗯,你几乎涵盖了它。考虑到所有这些,当我拼凑一个快速的一次性脚本并且它的行为正是我想要的时候,我倾向于使用glob,并使用opendirreaddir在正在进行的生产代码或库中,我可以花时间和更清晰,更清晰的代码是有帮助的。

答案 5 :(得分:3)

对于小而简单的事情,我更喜欢glob。就在前几天,我使用它和二十行perl脚本来重新占据我的音乐库的大部分。然而,glob有一个非常奇怪的名字。通配?就名字而言,它根本不直观。

我对readdir的最大挫折是它以一种对大多数人来说有点奇怪的方式处理目录。通常,程序员不会将目录视为流,而是将其视为glob提供的资源或列表。名称更好,功能更好,但界面仍然有待改进。

答案 6 :(得分:2)

这是一个非常全面的清单。 readdir(和readdir + grep)的开销低于glob,因此如果您需要分析大量目录,那么这对readdir来说是一个加号

答案 7 :(得分:2)

  

glob pros:

     

3)无需手动将目录名称添加到项目

例外:

say for glob "*";

--output:--
1perl.pl
2perl.pl
2perl.pl.bak
3perl.pl
3perl.pl.bak
4perl.pl
data.txt
data1.txt
data2.txt
data2.txt.out

据我所知,glob的规则是:您必须提供目录的完整路径才能获得完整路径。 Perl文档似乎没有提到这一点,这里也没有任何帖子。

这意味着当您只需要文件名(而不是完整路径)时,可以使用glob代替readdir,并且您不希望返回隐藏文件,即以'开头的文件。 ”。例如,

chdir ("../..");  
say for glob("*");

答案 8 :(得分:2)

在类似的说明中,File::Slurp有一个名为read_dir的函数。

由于我在脚本中使用了File::Slurp的其他功能,read_dir也成为一种习惯。

它还有以下选项:err_modeprefixkeep_dot_dot

答案 9 :(得分:1)

首先,做一些阅读。第9.6章。 Perl Cookbook的内容概述了我想要很好地解决的问题,就在讨论标题下。

其次,在Perl目录中搜索globdosglob。虽然可以使用许多不同的来源(获取文件列表的方法),但我指向dosglob的原因是,如果您碰巧在Windows平台上(并使用dosglob解决方案) ,它实际上是使用opendir / readdir / closedir。其他版本使用内置shell命令或预编译的OS特定可执行文件。

如果您知道自己正在定位特定平台,则可以使用此信息。仅供参考,我在Strawberry Perl Portable版本5.12.2上进行了调查,因此在新版本或原始版本的Perl上可能略有不同。