【问题标题】:How can I read the files in a directory in sorted order?如何按排序顺序读取目录中的文件?
【发布时间】:2009-01-12 17:22:18
【问题描述】:

当我在 Perl 中读取带有 opendirreaddirclosedir 的目录时,readdir 函数似乎没有以任何特定顺序读取文件(我知道)。

我正在读取一个目录,其中包含以纪元时间戳命名的子目录:

1224161460
1228324260
1229698140

我想按数字顺序读取这些目录,这会将最旧的目录放在第一位。

当我使用readdir时,它读取的第一个是1228324260,也就是中间的。我知道我可以将目录内容放在一个数组中并对数组进行排序,但是有没有可以传递给readdir 以按排序顺序读取的选项?或者也许比将所有内容推入数组并对数组进行排序更优雅的方式来实现这一点?可能也有模块可以做到这一点,但是很难在我们的环境中安装模块,所以除非它是内置模块,否则我宁愿不使用模块...

谢谢!

编辑 根据要求,我发布了我正在使用的代码:

opendir( my $data_dh, $data_dir ) or die "Cannot open $data_dir\n";
while ( my $name = readdir($data_dh) ) {
    next if ( $name eq '.' or $name eq '..' );
    my $full_path = "${data_dir}/${name}";
    next unless ( -d $full_path );
    process_dir($full_path);
}
closedir($data_dh);

【问题讨论】:

  • 我在 AIX 机器上使用 perl 5.8.2,如果有影响的话
  • 我知道这是一个简单的脚本,但你能发布你的代码,这样我们就不必重现它了吗?
  • process_dir 之前的 & 符号不是必需的,并且在某些情况下可能是有害的。我建议你删除它。
  • 你不应该做 "method() || die";当您将方法调用链接在一起时,“或”运算符就是您想要的。例如,“opendir() 或死“无法打开”
  • 谢谢罗伯特 - 我将开始使用“或”来代替

标签: perl directory readdir getdirectories


【解决方案1】:

readdir 可以在数组上下文中调用,所以只需这样做:

opendir( my $data_dh, $data_dir) or die "Cannot open $data_dir\n";
my @files = sort { $a <=> $b } readdir($data_dh);
while ( my $name = shift @files ) {
...

【讨论】:

  • 你不需要 { $a $b } 块;这里默认就够了。
  • 否,默认值不够。默认按词法排序,而不是按数字排序。 2001-09-09T01:46:40 之前的时间戳排序将比它之后的大多数时间戳晚,因为它打破了 1,000,000,000 秒的障碍。
  • 真的,opendir 在这里是多余的。您最好的选择是使用 glob() ...无需担心目录句柄。
  • @RobertP 使用glob 你会得到全名(除非$data_dir 恰好也是一个根)。这意味着要再拨打一次basenamechdir。后者可能是一个及时的问题。
  • 但是readdir 也可能很糟糕,即用于文件测试-X,因为你会丢失路径。
【解决方案2】:

你可以尝试一下 Glob 魔法,glob 似乎以一种排序的方式运行,所以:

#  Glob in scalar context iterates the result set internally
while( defined( my $dir = glob($dir . '/*' ) ) ){ 
     print $dir, "\n";
    # $dir is fed ordered and with full names. 
}

# Glob in list context returns all results. 
for( glob($dir.'/*' ) ){ 
  print $dir , "\n";
  # also ordered. 
}

应该可以。小心 glob,因为:

 for(0..20){ 
   printf "%30s|%30s\n", glob($dir.'/*' ), glob($dir.'/*' );
 }

做了一些半神奇的事情,并在每一行上打印 dir 内容两次。即:

/foo/bar/a  |  /foo/bar/a
/foo/bar/b  |  /foo/bar/b
/foo/bar/c  |  /foo/bar/c
/foo/bar/d  |  /foo/bar/d

【讨论】:

  • 这可能是最好的方法。 opendir 在这里真的是大材小用了。
【解决方案3】:

只需将sort 放在要重新排序的任何列表运算符前面。您也不需要将结果存储在数组中。你可以使用foreach:

opendir my($dh), $dirname or die "Could not open directory [$dirname]: $!";

foreach my $file ( sort { $a <=> $b } readdir $dh )
    {
    ...
    }

【讨论】:

  • ++ 为简化起见,但我认为有人破坏了您的代码标记(排序块)。
  • 啊,是的,pre 块不喜欢尖括号,尽管它在预览中表现得很好。
猜你喜欢
  • 1970-01-01
  • 2014-12-19
  • 1970-01-01
  • 1970-01-01
  • 2018-06-11
  • 1970-01-01
  • 2015-02-02
  • 2021-08-20
  • 1970-01-01
相关资源
最近更新 更多