这个问题是从这个问题中分拆出来的.一些历史:当我第一次学习Perl时,我总是使用glob
而不是opendir
+ readdir
因为我发现它更容易.后来各种帖子和读物表明这glob
很糟糕,所以现在我几乎总是使用readdir
.
在思考了这个最近的问题之后,我意识到我选择其中一个或另一个选择的理由可能是无聊的.所以,我将列出一些优点和缺点,我希望更有经验的Perl人可以插入并澄清.概括地说,问题是有没有令人信服的理由,更喜欢glob
到readdir
或readdir
到glob
(在某些或所有情况下)?
glob
优点:没有dotfiles(除非你要求他们)
物品的订单是有保证的
无需手动将目录名称添加到项目上
更好的名字(来吧- glob
与readdir
没有比赛,如果我们的名字单独判断)
(从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}}";
glob
缺点:旧版本只是简单的破坏(但是"较旧"意味着5.6之前的版本,我认为,坦率地说,如果你使用的是5.6之前的版本,你会遇到更大的问题)
stat
每次调用(即stat
在大多数情况下无用).
目录名称中的空格问题(这仍然是真的吗?)
(来自brian的回答)可以返回不存在的文件名:
$ perl -le 'print glob "{ab}{cd}"'
readdir
优点:(来自brian的回答)opendir
返回一个文件句柄,你可以在你的程序中传递(并重用),但glob
只返回一个列表
(布莱恩的回答)readdir
是正确的迭代器,并提供功能rewinddir
,seekdir
,telldir
快点?(根据上面的一些glob
特征进行纯粹的猜测.无论如何,我并不是真的担心这种优化水平,但它是理论专家.)
不太容易出现边缘情况的错误glob
?
默认情况下读取所有内容(dotfiles)(这也是一个骗局)
可能会说服你不要命名文件0
(con也 - 请参阅Brad的回答)
任何人?Bueller?Bueller?
readdir
缺点:如果您不记得预先添加目录名称,那么当您尝试进行文件测试或复制项目或编辑项目或...时,您会得到一些信息.
如果你不记得和项目一起grep
出去,那么当你计算项目时,你会得到一点,或者尝试递归地沿文件树行走或者.......
..
我是否提到过预先填写目录名称?(旁注,但我在Perl初学者邮件列表中的第一篇文章是经典的,"为什么这个涉及文件测试的代码在某些时候不起作用?"与此问题相关的问题.显然,我仍然很苦.)
物品不按特定顺序退回.这意味着您经常需要记住以某种方式对它们进行排序.(如果它意味着更快的速度,这可能是专业的,如果它意味着你实际上考虑如何以及是否需要对项目进行排序.)编辑:可疑的小样本,但在Mac readdir
上按字母顺序返回项目,不区分大小写.在Debian盒子和OpenBSD服务器上,订单是完全随机的.我用Apple的内置Perl(5.8.8)和我自己编译的5.10.1测试了Mac.Debian框是5.10.0,OpenBSD机器也是如此.我想知道这是文件系统问题,而不是Perl?
默认情况下读取所有内容(dotfiles)(这也是专业版)
不一定处理一个名为的文件0
(参见专业人士 - 请参阅Brad的回答)
brian d foy.. 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}"'
如果目录很大(包含大量文件),或者在程序运行时它正在更改(文件被创建和删除),则列表与目录句柄可能很重要. (7认同)
当然你可以重载glob,但为什么你呢?如果您不想使用它,只需使用自己的子程序即可完全按照内置单独使用的方式执行操作. (2认同)
ysth.. 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?
Brad Gilbert.. 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
语句中添加了另一个元素.
你错过了它们之间最重要,最大的区别: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}"'
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?
下面是一个缺点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
语句中添加了另一个元素.
glob
可以方便地读取给定固定深度的所有子目录,如glob "*/*/*"
.我在几个场合都发现了这个方便.