使用Perl对目录文件名进行排序

3
我想按名称对目录中的文件进行排序/打印。我的代码列出了所有文件,但排序有偏差。以下是我的代码和结果。欢迎提出任何建议!
my $file;
opendir (DIR, "$dir");
while ($file = readdir(DIR)) {
    push (my @files, $file);
    @files = sort {$a cmp $b} @files;   #NOT sorting!
    foreach $file (@files) {
        print "$file\n";
    }
}

以下是“排序后”的结果:

Screenshot-Chess_-_Human_versus_GNUchess.png  
test.html  
katyperry.gif  
test.cgi  
Californication.S04E05.HDTV.XviD-ASAP.avi  
FreeWatch_13.exe  
proxy.jpg  
test.pl-  
.  
attachment2.jpg  
attachment.jpg  
Californication.S04E06.HDTV.XviD-LOL.avi  
Californication.S04E07.HDTV.XviD-LOL.avi  
boxter.jpg  
..  
7个回答

12
您正在构建一系列单元素列表,对每个列表进行排序(这是无操作),然后打印它。您想要做的是将所有文件的列表读入一个列表中,然后对其进行排序,就像这样:
您正在构建一系列单元素列表,对每个列表进行排序(这是无操作),然后打印它。您想要做的是将所有文件的列表读入一个列表中,然后对其进行排序,就像这样:
my $file;
my @files;
opendir (DIR, "$dir");
while ($file = readdir(DIR)) {
    push (@files, $file);
}
@files = sort {$a cmp $b} @files;
foreach $file (@files) {
    print "$file\n";
}

4
最好用@files = readdir(DIR)替换那个while循环。 - Andy Lester
4
@andy-lester:就这个问题而言,print join("\n", sort { $a cmp $b } readdir(DIR))."\n"; - Anomie
谢谢!我简直不敢相信自己会错过这么基础的东西。感谢StackOverflow和所有帮助过我的人! - superfry
1
同时添加 lc 函数可以避免因名称大小写而导致的奇怪结果:@files = sort {lc($a) cmp lc($b)} @files; - superfry

5

另一种方法是使用File::Slurp

use warnings; 
use strict; 
use File::Slurp;
use Data::Dumper;

my @files = read_dir($dir);
@files = sort @files;
print Dumper(\@files);

这段代码负责打开和关闭目录,检查是否成功,并自动排除特殊的.和..目录,这些通常是不需要的。

3

while循环内的词法作用域中的my @files将总是在循环的每次迭代中创建一个新的@files数组。因此,任何时候,@files仅包含单个元素,因此排序是无意义的。现在请查看Anomie的答案


2
严肃地说,您正在做比您应该做的更多的工作。
use warnings;
use strict;
use autodie;
use File::Spec::Functions qw'no_upwards';

my $dir = '.';

opendir my($dh), $dir;
for my $file ( no_upwards sort readdir $dh ){
  print "$file\n";
}
closedir $dh;

2

路径排序很棘手,因为在ASCII字符排序中,路径分隔符“/”位于大多数路径字符之前,但并不是所有字符之前,尤其是点号和破折号。

通过分割斜线来将路径分解成路径元素。使用cmp按字母数字顺序比较路径元素。如果相等,则具有较少元素的路径排在具有更多元素的路径之前。

一定要去掉任何换行符。使用Perl sort命令的&bypath子例程进行排序:sort bypath @files;

sub bypath {
  my @a = split m'/', $a;
  my @b = split m'/', $b;
  for ( my $i = 0; $i<=$#a; $i++ ) {
    last if $i > $#b;
    return $a[$i] cmp $b[$i] if $a[$i] cmp $b[$i];
    }
  return $#a <=> $#b;
  }

示例结果:

  • this
  • this/that
  • this/that/other
  • this/that/other/filea
  • this/that/other/fileb
  • this/that/other.new/filea

0

我不明白为什么你们这样复杂地做。这对于排序文件或文件夹非常完美。

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");
}

0
use v5.10;
say for sort do { opendir( my $fh, $dir ); readdir($fh) };

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