在 Perl 中有啥理由更喜欢 glob 而不是 readdir(反之亦然)?
Posted
技术标签:
【中文标题】在 Perl 中有啥理由更喜欢 glob 而不是 readdir(反之亦然)?【英文标题】:What reasons are there to prefer glob over readdir (or vice-versa) in Perl?在 Perl 中有什么理由更喜欢 glob 而不是 readdir(反之亦然)? 【发布时间】:2010-12-03 04:04:53 【问题描述】:这个问题是this one 的衍生问题。一些历史:当我第一次学习 Perl 时,我几乎总是使用 glob
而不是 opendir
+ readdir
,因为我发现它更容易。后来各种帖子和阅读表明glob
不好,所以现在我几乎总是使用readdir
。
在考虑了this recent question 之后,我意识到我选择其中一个或另一个的原因可能是愚蠢的。所以,我将列出一些优点和缺点,我希望更有经验的 Perl 人员能够加入并澄清。简而言之,问题是有令人信服的理由更喜欢glob
而不是readdir
或readdir
而不是glob
(在某些或所有情况下)?
glob
专业人士:
-
没有点文件(除非您要求)
保证商品顺序
无需手动将目录名称添加到项目中
更好的名字(来吧 -
glob
与 readdir
相比,如果我们仅根据名字来判断,那是没有竞争力的)
(来自 ysth 的回答;参见下面的glob
cons 4)可以返回不存在的文件名:
@deck = glob "A,K,Q,J,10,9,8,7,6,5,4,3,2\x2660,\x2665,\x2666,\x2663";
glob
缺点:
-
旧版本完全被破坏(但“旧”是指 5.6 之前的版本,我认为,坦率地说,如果您使用的是 5.6 之前的 Perl,您会遇到更大的问题)
每次都调用
stat
(即,在大多数情况下,stat
的使用是无用的)。
目录名称中的空格问题(这仍然是真的吗?)
(来自 brian 的回答)可以返回不存在的文件名:
$ perl -le 'print glob "abcd"'
readdir
专业人士:
-
(来自 brian 的回答)
opendir
返回一个文件句柄,您可以在程序中传递它(并重复使用),但 glob
只返回一个列表
(来自 brian 的回答)readdir
是一个适当的迭代器,并为rewinddir
、seekdir
、telldir
提供函数
更快? (基于以上glob
的一些功能的纯猜测。无论如何,我并不担心这种优化水平,但这是理论上的专业人士。)
比glob
更不容易出现边缘情况错误?
默认读取所有内容(点文件也是)(这也是个骗局)
可能会说服您不要将文件命名为 0
(也是一个骗局 - 请参阅 Brad 的回答)
有人吗?布勒?比勒?
readdir
缺点:
-
如果您不记得添加目录名称,那么当您尝试进行文件测试或复制项目或编辑项目或...时,您会得到一点信息...
如果您不记得将
grep
排除在 .
和 ..
项目之外,那么当您计数项目时,您会得到一点,或者尝试递归地遍历文件树或...
我有没有提到在目录名称前面加上前缀? (旁注,但我在 Perl Beginners 邮件列表的第一篇文章是经典的,“为什么涉及文件测试的这段代码有时不起作用?”与这个问题相关的问题。显然,我仍然很痛苦。)
物品的退回没有特定的顺序。这意味着您通常必须记住以某种方式对它们进行排序。 (如果这意味着更快的速度,并且如果这意味着您实际上考虑关于如何以及是否需要对项目进行排序,这可能是一个专业人士。)编辑:非常小的样本,但在 Mac 上 readdir
按字母顺序返回项目,不区分大小写。在 Debian 机器和 OpenBSD 服务器上,顺序是完全随机的。我用 Apple 的内置 Perl (5.8.8) 和我自己编译的 5.10.1 测试了 Mac。 Debian 机器是 5.10.0,OpenBSD 机器也是。我想知道这是否是文件系统问题,而不是 Perl?
默认读取所有内容(点文件也是)(这也是专业人士)
不一定能很好地处理名为 0
的文件(另请参阅专业人士 - 请参阅 Brad 的回答)
【问题讨论】:
在装有 Perl 5.10.1 的 Mac 上,我能够创建一个包含空格的目录,并且 glob 将其作为列表的一部分返回。我什至在其中创建了一个带有换行符的目录名称,并且它起作用了。 :) 【参考方案1】:这是一个非常全面的列表。 readdir
(和readdir
+ grep
)的开销比glob
少,因此如果您需要分析大量目录,这对readdir
来说是一个优势。
【讨论】:
【参考方案2】:嗯,你几乎涵盖了它。考虑到所有这些,当我将一个快速的一次性脚本放在一起时,我倾向于使用glob
,它的行为正是我想要的,并在正在进行的生产代码或库中使用opendir
和readdir
我可以花时间在哪里,更清晰、更简洁的代码会很有帮助。
【讨论】:
【参考方案3】:你错过了它们之间最重要、最大的区别: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 "abcd"'
【讨论】:
感谢您帮助我了解有关文件句柄与列表的要点。 如果目录很大(有很多文件)或在程序运行时它正在更改(文件被创建和删除),则列表与目录句柄可能很重要。 +1 to @Loadmaster -- 我唯一一次注意到 glob v readdir 是当我在目录中有很多文件 (>10,000) 时。 glob 也有一个标量上下文。如果您使用 readdir 一次创建大列表,您将遇到同样的问题。 当然你可以重载 glob,但你为什么会呢?如果您不想使用它,只需制作自己的子程序来完全按照您的需要做,而不要使用内置。【参考方案4】:对于小而简单的事情,我更喜欢glob
。就在前几天,我用它和一个二十行的 perl 脚本重新标记了我的音乐库的大部分。然而,glob
有一个很奇怪的名字。全球?就名称而言,它根本不直观。
我对@987654323@ 的最大困扰是它以一种对大多数人来说有些奇怪的方式处理目录。通常,程序员不会将目录视为流,而是将其视为 glob 提供的资源或列表。名称更好,功能更好,但界面仍有不足之处。
【讨论】:
口味各不相同,但我发现readdir
有点闷(作为一个名字)和glob
差不多。再说一次,我喜欢 Ruby 的 splat
运算符(作为名称和其他),所以我想我很奇怪。
我绝对同意 glob 是一个很棒的名字...我只是希望它更直观:)【参考方案5】:
glob 专业人士:可以返回不存在的“文件名”:
my @deck = List::Util::shuffle glob "A,K,Q,J,10,9,8,7,6,5,4,3,2\x2660,\x2665,\x2666,\x2663";
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♦
【讨论】:
我认为这是一个骗局。这是创建一副纸牌的非常聪明的方法,但是我很难考虑更严重的情况,我需要伪文件名(但我可以想象很多我不希望返回文件名的情况不存在)。 @Telemachus:它既是优点也是缺点。每当您使用 语法并且只需要现有文件时,您都需要过滤结果,但这在 IMO 中是非常值得的。 (我知道我经常做cp -a dirname,.orig
之类的事情。)
当您想要创建文件名时,它有时会很有用。典型的用例是创建目录层次结构。例如,在 shell 中你可能会做 mkdir -p /home/alice,bob,charlie/public_html,mail,docs
【参考方案6】:
全球专业人士:
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("*");
【讨论】:
【参考方案7】:glob
可以方便地读取给定固定深度的所有子目录,如glob "*/*/*"
。我曾多次发现这很方便。
【讨论】:
【参考方案8】:这是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
语句中添加了另一个元素。
【讨论】:
这是一个需要注意的很好的边缘案例,但我不确定我对创建像 0 这样的文件名的人有多少同情。一旦你沿着这条路走下去,这只是一小步文件名为 ` 你看到这个文件开头的两个空格了吗?.txt. Still, I'll note it in the
readdir` 缺点。
对于each
,5.18 版将进行同样的修复。【参考方案9】:
首先,做一些阅读。第 9.6 章。的Perl Cookbook 概述了我想要很好地表达的观点,就在讨论标题下方。
其次,在您的 Perl 目录中搜索 glob
和 dosglob
。虽然可以使用许多不同的来源(获取文件列表的方法),但我将您指向 dosglob
的原因是,如果您碰巧在 Windows 平台上(并使用 dosglob
解决方案),它实际上是使用opendir
/readdir
/closedir
。其他版本使用内置的 shell 命令或预编译的操作系统特定的可执行文件。
如果您知道自己针对的是特定平台,则可以利用这些信息来发挥自己的优势。仅供参考,我在 Strawberry Perl Portable edition 5.12.2 上对此进行了研究,因此在更新或原始版本的 Perl 上情况可能略有不同。
【讨论】:
【参考方案10】:类似地,File::Slurp
有一个名为read_dir
的函数。
由于我在脚本中大量使用File::Slurp
的其他功能,read_dir
也成为了一种习惯。
它还具有以下选项:err_mode
、prefix
和 keep_dot_dot
。
【讨论】:
以上是关于在 Perl 中有啥理由更喜欢 glob 而不是 readdir(反之亦然)?的主要内容,如果未能解决你的问题,请参考以下文章
在 CSS 中对 RGB 颜色值使用十六进制而不是十进制有啥好的理由吗?
对于更新不依赖于先前值的不可变集合,是不是有任何理由更喜欢 Interlocked 而不是 volatile?