Perl按顺序读取目录内容

8

有没有办法保证readdir返回的列表中的顺序?

我有以下代码:

opendir(my $DIR, $src) or die "Error opening $src";

# Loop for each file in the directory
while (my $file = readdir($DIR))
{
        print "$file\n";
    }

但是它们以随机顺序返回。现在我知道通过快速的谷歌搜索可以找到很多解决方案,但是我找不到我需要的精确排序方式。 基本上,我希望文件夹首先或最后出现,而不是出现在文件中间。
例如,如果我当前有以下文件夹结构:
folder
folder
file1
file2
file3

我得到了结果:
file2
folder
folder
file1
file3

实际上我想要的是:

folder
folder
file1
file2
file3

或者:

file1
file2
file3
folder
folder

有什么方法可以实现这个吗?

应该使用某种排序算法。 - mpapec
4个回答

6

你可以按文件夹优先的顺序排序,然后按文件/目录名称进行排序。

# $src pointing to folder open with opendir
my @sorted_dir = 
  map $_->[0],
  sort {
    $a->[1] <=> $b->[1]
      ||
    $a->[0] cmp $b->[0]
  }
  map [ $_, -f "$src/$_" ],
  readdir($DIR);

虽然可以通过类似的方法实现相同的效果,

for my $file (sort { -f "$src/$a" <=> -f "$src/$b" } readdir($DIR)) {
  print "$file\n";
}

由于它更频繁地检查目录项是否是普通文件,因此它速度较慢且效率低下。


谢谢,不过我还是希望有更优雅的解决方案。如果别的方法都失败了,我会退而求其次使用这种方法 :) - Travv92
更新为简单但更慢的替代方案。 - mpapec
谢谢,这样看起来更好了。另外,simbabque提供的其他解决方案也有同样的问题,缺少了$src/,所以它无法正常工作! - Travv92
还要注意第二个不按条目名称排序。 - mpapec

5

foreach (sort readdir $dh) {} 对我来说很有效。

例如:

opendir (my $DIR, "$dir") || die "Error while opening $dir: $!\n";

foreach my $dirFileName(sort readdir $DIR)
{
      next if $dirFileName eq '.' or $dirFileName eq '..';
      print("fileName: $dirFileName ... \n");
}

5
您可以使用sort来完成此操作,方法是查看readdir返回的列表中的每个条目。
opendir(my $DIR, '.') or die "Error opening ";

foreach my $file (sort { -d $a <=> -d $b } readdir($DIR)) {
  print "$file\n";
}

这将使文件夹排在最后。

您好,当我这样做时,会收到 Use of uninitialized value in numeric comparison (<=>) 的错误提示。 - Travv92
尝试使用 cmp 而不是 <=> - innaM
现在我得到了 Use of uninitialized value in string comparison (cmp) 的错误信息。从两者中删除 -d 不会产生任何错误(但很明显输出是错误的)。 - Travv92
1
看起来readdir的返回值中有空内容。顺便说一下,<=>是正确的。cmp是字母数字比较,而-d只返回01,所以不需要使用cmp - simbabque

2
您可以使用List::MoreUtils中的part函数。
#!/usr/bin/env perl

use strict;
use warnings;

use List::MoreUtils 'part';

my $dir = shift || '.';

opendir my $dh, $dir or die "Cannot open $dir";

my ($files, $dirs) = part { -d } sort readdir $dh;

print "$_\n" for @$files, @$dirs;

另一个想法是,您可以查看File::Next


它有效,易于阅读,并且你几乎肯定已经拥有它了。但是...由你决定。 - Joel Berger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接