如何按字母中的数字对字符串列表进行排序?

Ste*_*eve 14 sorting perl numbers natural-sort

毫无疑问,这对你来说很简单......

我有一个类似的文件名列表;

fw_d.log.1.gz  
through  
fw_d.log.300.gz  
Run Code Online (Sandbox Code Playgroud)

当我使用下面的代码块时,它几乎按我想要的方式排序,但不完全.

#!/usr/bin/perl -w
my $basedir = "/var/log";
my @verdir = qw(fw_d);
my $fulldir;
my $configs;
my $combidir;

foreach $combidir (@verdir) {
    $fulldir = "$basedir/$combidir";
    opendir (DIR, $fulldir);
    my @files = grep { $_ ne '.' && $_ ne '..' && $_ ne 'CVS' readdir DIR;
    closedir (DIR);
    @files1 = sort {$a cmp $b}(@files);
    foreach my $configs (@files1) {
        print "Checking $configs\n";
        system("less $basedir/$combidir/$configs | grep \'.* Group = , Username = .* autheauthenticated.\' >> output.log" );
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个片段输出

Checking fw_d.log  
Checking fw_d.log.1.gz  
Checking fw_d.log.10.gz  
Checking fw_d.log.100.gz  
Checking fw_d.log.101.gz  
Checking fw_d.log.102.gz  
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,它几乎按照我希望的方式排序....有没有人有任何建议,无论是阅读,还是我可以使用的代码片段?

提前致谢.
史蒂夫.

Tot*_*oto 21

你可以使用Schartzian-transform:

my @sorted = map  { $_->[0] }
             sort { $a->[1] <=> $b->[1] }
             map  { [$_, $_=~/(\d+)/] }
                 @files;
print Dumper \@sorted;
Run Code Online (Sandbox Code Playgroud)

添加了Schwartzian-Transform和子程序之间比较的基准

use Benchmark qw(:all);

# build list of files
my @files = map {'fw_d.log.'.int(rand()*1000).'.log' } 0 ..300;

my $count = -3;
my $r = cmpthese($count, {
        'subname' => sub {
              sub expand {
                   my $file=shift; 
                   $file=~s{(\d+)}{sprintf "%04d", $1}eg;
                   return $file;
              }
              my @sorted = sort { expand($a) cmp expand($b) } @files;
        },
        'schwartzian' => sub {
              my @sorted = map  { $_->[0] }
                           sort { $a->[1] <=> $b->[1] }
                           map  { [$_, $_=~/(\d+)/] }
                 @files;
         }
});
Run Code Online (Sandbox Code Playgroud)

结果:

              Rate     subname schwartzian
subname     21.2/s          --        -92%
schwartzian  279/s       1215%          --
Run Code Online (Sandbox Code Playgroud)

Schwartzian变换对300个文件的排序效率提高了约13倍.


mir*_*rod 10

问题是代码执行了您告诉它的操作:按字母顺序对文件名进行排序.

此时应更换sort { $a cmp $b }sort { expand($a) cmp expand($b) }

expand:

sub expand 
   { my $file=shift; 
     $file=~s{(\d+)}{sprintf "%04d", $1}eg; # expand all numbers to 4 digits
     return $file;
   }
Run Code Online (Sandbox Code Playgroud)

  • 注意:这只适用于整数; 当你有像1.1和1.02和1.201等十进制数字时,它会出错.签名号码也是一个问题.我确实有代码可以为这些做正确的事情,但它太大了,不适合评论.:)你可以在这里找到[一个相当复杂的原型](http://training.perl.com/scripts/FixStrings.pm),虽然它也做了其他几件事,比如标准化罗马数字和用拉丁文写的希腊文字母. (2认同)

Nat*_*man 6

您可以尝试使用自定义排序功能:

sub sort_by_number {
    $a =~ /(\d+)/;
    $numa = $1;
    $b =~ /(\d+)/;
    $numb = $1;

    return $numa <=> $numb;
}
Run Code Online (Sandbox Code Playgroud)

然后像这样排序:

@files1 = sort sort_by_number @files;
Run Code Online (Sandbox Code Playgroud)

这将按@files每个字符串中第一个数字的值对字符串进行排序.

  • 模式"/(\ d + \)"应更改为"/(\ d +)" (2认同)