62

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

在考虑了最近的这个问题后,我意识到我选择一个或另一个的原因可能是愚蠢的。所以,我将列出一些优点和缺点,我希望更有经验的 Perl 人员能够加入并澄清。简而言之,问题是是否有令人信服的理由偏爱globreaddirreaddirglob某些或所有情况下)?

glob优点:

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

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

glob缺点:

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

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

readdir优点:

  1. (来自布赖恩的回答)opendir返回一个文件句柄,您可以在程序中传递(并重用),但glob只返回一个列表
  2. (来自布赖恩的回答)readdir是一个合适的迭代器,并提供函数到rewinddir, seekdir,telldir
  3. 快点?(基于上面的一些glob特性的纯粹猜测。无论如何我并不担心这种优化水平,但它是一个理论上的专业人士。)
  4. 比 ? 更不容易出现边缘情况错误glob
  5. 默认情况下读取所有内容(也是点文件)(这也是一个骗局)
  6. 可能会说服您不要命名文件0(也是一个骗局 - 参见布拉德的回答)
  7. 任何人?布勒?布勒?

readdir缺点:

  1. 如果您不记得添加目录名称,当尝试进行文件测试或复制项目或编辑项目或...
  2. 如果你不记得grep...项目,当你计算项目时你得到一点,或者尝试递归地沿着文件树走或者......
  3. 我有没有提到在目录名称前面加上前缀?(旁注,但我在 Perl Beginners 邮件列表中的第一篇文章是经典的,“为什么涉及文件测试的代码在某些时候不起作用?”与这个问题相关的问题。显然,我仍然很痛苦。)
  4. 退回的物品没有特定的顺序。这意味着您通常必须记住以某种方式对它们进行排序。(如果它意味着更快的速度,并且如果它意味着您真正考虑如何以及是否需要对项目进行排序,这可能是一个专业人士。)编辑:非常小的样本,但在 Macreaddir上按字母顺序返回项目,不区分大小写。在 Debian 机器和 OpenBSD 服务器上,顺序是完全随机的。我用 Apple 的内置 Perl (5.8.8) 和我自己编译的 5.10.1 测试了 Mac。Debian 机器是 5.10.0,OpenBSD 机器也是。我想知道这是否是文件系统问题,而不是 Perl?
  5. 默认情况下读取所有内容(也是点文件)(这也是专业人士)
  6. 不一定能很好地处理名为的文件0(另见专业人士 - 见布拉德的回答)
4

10 回答 10

43

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

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

使用 dirhandle,你有一个可控的迭代器,你可以在其中移动seekdir,尽管glob你只是得到下一个项目。

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

其他一些要记住的事情:

  • 您可以使用 实现自己的 glob opendir,但不能反过来。

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

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

    $ perl -le 'print glob "{ab}{cd}"'
    
于 2009-10-01T22:39:00.857 回答
7

glob 优点:可以返回不存在的“文件名”:

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♦
于 2009-10-02T02:33:33.697 回答
6

glob可以方便地读取给定固定深度的所有子目录,如glob "*/*/*". 我曾多次发现这很方便。

于 2010-03-01T23:30:16.510 回答
6

这是opendir和的一个缺点readdir

{
  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. 在我的电脑上打印251,并且188,使用 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语句中添加了另一个元素。

于 2010-03-02T17:46:08.080 回答
3

好吧,你几乎涵盖了它。考虑到所有这些,glob当我将一个快速的一次性脚本放在一起时,我倾向于使用它,它的行为正是我想要的,opendirreaddir在正在进行的生产代码或库中使用,我可以花时间更清楚地使用,更简洁的代码很有帮助。

于 2009-10-01T22:29:05.247 回答
2

这是一个非常全面的清单。readdir(and readdir+ grep) 的开销比 and 少,因此如果您需要分析大量目录glob,这是一个加分项。readdir

于 2009-10-01T22:28:10.287 回答
2

对于小而简单的事情,我更喜欢glob. 就在前几天,我使用它和一个 20 行的 perl 脚本重新标记了我的音乐库的大部分。 glob但是,有一个很奇怪的名字。全球?就名称而言,它根本不直观。

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

于 2009-10-01T22:51:34.827 回答
2

全球优势:

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("*");
于 2009-11-14T06:44:31.600 回答
2

在类似的注释中,File::Slurp有一个名为read_dir.

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

它还具有以下选项:err_modeprefixkeep_dot_dot

于 2012-06-07T08:12:59.000 回答
0

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

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

如果您知道自己针对的是特定平台,则可以利用此信息来发挥自己的优势。仅供参考,我在 Strawberry Perl Portable edition 5.12.2 上对此进行了研究,因此在更新或原始版本的 Perl 上情况可能略有不同。

于 2011-05-19T01:15:02.347 回答