有什么理由在Perl中使用glob而不是readdir(反之亦然)?
这个问题是从这个问题分离出来的。 一些历史:当我第一次学习Perl时,我几乎总是使用glob
而不是opendir
+ readdir
因为我发现它更容易。 然后后来的各种post和阅读build议glob
是坏的,所以现在我几乎总是使用readdir
。
在思考了最近的这个问题之后,我意识到,我select其中一个的原因可能是混帐。 所以,我会列举一些优点和缺点,希望有更多经验的Perl人士可以加以澄清。 简而言之,问题是有没有令人信服的理由,喜欢glob
到readdir
或readdir
到glob
(在一些或所有情况下)?
glob
优点:
- 没有dotfiles(除非你要求)
- 项目的顺序是有保证的
- 不需要手动将目录名称预先添加到项目上
- 更好的名字(来吧 –
glob
与readdir
不是比赛,如果我们只是通过名字来判断) -
(来自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
缺点:
- 旧版本只是简单的破解(但“老”意味着前5.6,我认为,坦率地说,如果你使用5.6 Perl以前,你有更大的问题)
- 每次调用
stat
(即在大多数情况下无效使用stat
)。 - 目录名称中的空格问题(这仍然是真的吗?)
-
(来自brian的答案)可以返回不存在的文件名:
$ perl -le 'print glob "{ab}{cd}"'
readdir
优点:
- (从brian的答案)
opendir
返回一个文件句柄,你可以在程序中传递(和重用),但glob
只是返回一个列表 - (来自brian的答案)
readdir
是一个适当的迭代器,并提供函数rewinddir
,seekdir
,telldir
- 更快? (根据上面一些
glob
的特点来猜测,我并不是很担心这个优化级别,但是这是一个理论上的专家)。 - 比
glob
更less出现边界错误? - 读取一切(dotfiles也是默认)(这也是一个骗局)
- 可能会说服你不要命名一个文件
0
(一个con也 – 见布拉德的答案) - 任何人? Bueller? Bueller?
readdir
缺点:
- 如果您不记得预先指定目录名称,那么当您尝试执行文件testing或复制项目或编辑项目或…
- 如果你不记得要清理掉
.
和..
项目,你会得到一点,当你计数项目,或尝试recursion地走下文件树或… - 我有没有提到预先的目录名称? (一个旁注,但我第一次发布Perl初学者的邮件列表是一个经典的,“为什么这个代码涉及filetests不能正常工作?”与这个问题有关的问题。显然,我还是很苦恼。
- 项目没有特定的顺序返回。 这意味着您经常需要记住以某种方式对其进行sorting。 (这可能是一个专业,如果这意味着更多的速度,如果这意味着你真的想如何,如果你需要对项目进行sorting)。 编辑 :可怕的小样本,但在Mac上
readdir
按字母顺序返回项目,不区分大小写。 在Debian框和OpenBSD服务器上,顺序完全是随机的。 我用苹果内置的Perl(5.8.8)和我自己编译的5.10.1testing了Mac。 Debian框是5.10.0,与OpenBSD机一样。 我不知道这是一个文件系统问题,而不是Perl? - 读取一切(dotfiles)默认(这也是一个专业人士)
- 不一定很好地处理一个名为
0
的文件(请参阅专业人士 – 请参阅Brad的答案)
你错过了它们之间最重要,最大的区别: 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}"'
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♦
这是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进行testing
这个问题也使得它只是打印出一堆空行,无论文件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
语句。
glob
可以方便地读取给定固定深度的所有子目录,如glob "*/*/*"
。 我几次发现这个方便。
那么,你几乎覆盖了它。 考虑到所有这些,当我把一个快速的一次性脚本扔到一起时,我倾向于使用glob
,而且它的行为正是我想要的,并且在正在进行的生产代码或库中使用opendir
和readdir
,我可以花时间更清晰,更干净的代码是有帮助的。
对于小而简单的事情,我更喜欢glob
。 就在那一天,我用它和一个二十行perl脚本来重build我的音乐库的很大一部分。 glob
,但是,有一个很奇怪的名字。 通配? 就一个名字而言,这并不直观。
我对readdir
最大的readdir
就是它以一种对大多数人来说有点奇怪的方式来对待一个目录。 通常,程序员不会将目录视为stream,而是将其视为资源或列表,这是由glob提供的。 名称越好,function越好,但界面仍然有些不尽人意的地方。
这是一个非常全面的名单。 readdir
(和readdir
+ grep
)的开销比glob
,所以如果你需要分析大量的目录,这对readdir
来说是一个readdir
select。
全球优点:
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
的规则是:您必须提供目录的完整path以获取完整path。 Perl文档似乎没有提到,也没有在这里的任何职位。
这意味着当你只需要文件名(而不是完整path)时,可以使用glob
代替readdir
,并且不需要隐藏文件返回,即以'。'开始。 例如,
chdir ("../.."); say for glob("*");
首先,做一些阅读。 第9.6章。 的Perl Cookbook概述了我想要得到的好点,就在讨论标题下。
其次,在你的Perl目录下searchglob
和dosglob
。 虽然可以使用许多不同的来源(获取文件列表的方式),但是我指出你使用dosglob
的原因是,如果你碰巧在Windows平台上(并使用dosglob
解决scheme),那实际上是使用opendir
/ readdir
/ closedir
。 其他版本使用内置的shell命令或预编译的操作系统特定的可执行文件。
如果你知道你正在瞄准一个特定的平台,你可以使用这个信息,以利于您。 仅供参考,我在“草莓Perl便携版”5.12.2上对此进行了研究,所以对于较新的或原始版本的Perl,可能会有所不同。
在类似的说明中, File::Slurp
read_dir
有一个名为read_dir
的函数。
由于我在脚本中使用了File::Slurp
read_dir
的其他函数,因此read_dir
也成为一种习惯。
它也有以下选项: err_mode
, prefix
和keep_dot_dot
。