按数值对文件名进行排序

时间:2014-08-08 14:39:38

标签: perl sorting filenames numerical

序言:我讨厌提出这样的问题,但我坚持认为只是学习Perl ......看起来很容易,但我不知道在哪里看。

我有一个包含大量xml文件的文件夹,这些文件都被命名为“.xml”。 我需要按照数字顺序处理这些文件,因此“9123.xml”应该出现在“2384747.xml”之前。 我已成功按字母顺序对列表进行了排序:

opendir(XMLDIR,$xmldirname);
my @files = sort {$a cmp $b} readdir(XMLDIR);

但这不是我需要的。

我也试过

my @files = sort {$a <=> $b} readdir(XMLDIR);

显然失败了,因为文件名包含“.xml”并且不是整数数字。

有人可以敞开心扉,为我节省一周浏览Perl手册吗?

4 个答案:

答案 0 :(得分:4)

尽管你声称,sort { $a <=> $b } readdir(XMLDIR)仍有效。当Perl将字符串2384747.xml视为数字(如<=>所做的那样)时,它被视为具有值2384747

$ perl -wE'say 0+"2384747.xml"'
Argument "2384747.xml" isn't numeric in addition (+) at -e line 1.
2384747

当然,这些警告是个问题。您接受的解决方案会尝试删除它们,但未能删除所有这些解决方案,因为它没有考虑readdir将返回...。你必须先删除你不想要的文件。

以下是两个简单的解决方案:

my @files =
   sort { no warnings 'numeric'; $a <=> $b }
      grep { /^(\d)\.xml/ }
         readdir(XMLDIR);

my @files =
   sort { ( $a =~ /(\d+)/ )[0] <=> ( $b =~ /(\d+)/ )[0] }
      grep { /^(\d)\.xml/ }
         readdir(XMLDIR);

在这种特殊情况下,您可以优化代码:

my @files =
   map { "$_.xml" }             # Recreate the file name.
      sort { $a <=> $b }        # Compare the numbers.
         map { /^(\d)\.xml/ }   # Extract the number from desired files.
            readdir(XMLDIR);

最简单,最快速的解决方案是使用自然排序。

use Sort::Key::Natural qw( natsort );

my @files = natsort grep !/^\.\.?/, readdir(XMLDIR);

答案 1 :(得分:2)

你实际上非常接近。只需在比较中删除“.xml”:

opendir(XMLDIR,$xmldirname);
my @files = sort {substr($a, 0, index($a, '.')) <=> substr($b, 0, index($b, '.'))} readdir(XMLDIR);

答案 2 :(得分:1)

问题是<=>无法处理非完全数字的内容,事实上如果你use warnings;在运行时会得到类似于此的消息 - 时间:

  

Argument&#34; 11139.xml&#34;在testsort.pl第9行的sort中不是数字。

可以做的是将文件名从扩展名中分离出来,在文件名中按数字排序然后重新组合扩展名。这可以通过Schwartzian transform相当简单地完成:

use strict;
use warnings; 

use Data::Dumper; 

# get all of the XML files
my @xml_files = glob("*.xml");

print 'Unsorted: ' . Dumper \@xml_files; 
@xml_files = map  { join '.', @$_ }              # join filename and extension
             sort { $a->[0] <=> $b->[0] }        # sort against filename
             map  { [split /\./] } @xml_files;   # split on '.'
print 'Sorted: ' . Dumper \@xml_files; 

__END__
Unsorted: $VAR1 = [
          '11139.xml',
          '18136.xml',
          '28715.xml',
          '6810.xml',
          '9698.xml'
        ];
Sorted: $VAR1 = [
          '6810.xml',
          '9698.xml',
          '11139.xml',
          '18136.xml',
          '28715.xml'
        ];

答案 3 :(得分:1)

my @files =  sort {
    my ($x) = split /\./, $a;
    my ($y) = split /\./, $b;
    $x <=> $y
} readdir(XMLDIR);

或没有临时变量:

my @files =  sort {(split /\./, $a)[0] <=> (split /\./, $b)[0]} readdir(XMLDIR);