Как я могу отсортировать список строк по номерам в них?
Легкий, без сомнения, для вас...
У меня есть список имен файлов, которые похожи на это:
fw_d.log.1.gz
through
fw_d.log.300.gz
Когда я использую этот ниже блок кода, он почти сортирует его так, как я хочу, но не совсем.
#!/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" );
}
}
здесь выведен фрагмент
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
Как вы можете видеть, он почти сортирует его, как я надеялся... Есть ли у кого-нибудь какие-либо предложения, как при чтении, так и в фрагменте кода, который я могу использовать?
Спасибо заранее.
Стив.
Ответы
Ответ 1
Вы можете использовать Schartzian-transform:
my @sorted = map { $_->[0] }
sort { $a->[1] <=> $b->[1] }
map { [$_, $_=~/(\d+)/] }
@files;
print Dumper \@sorted;
Добавлен тест для сравнения между Шварццианским преобразованием и подпрограммой
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;
}
});
Результат:
Rate subname schwartzian
subname 21.2/s -- -92%
schwartzian 279/s 1215% --
Schwartzian-transform примерно в 13 раз эффективнее для сортировки 300 файлов.
Ответ 2
проблема в том, что код выполняет то, что вы ему скажите: сортируйте имена файлов в алфавитном порядке.
Вы должны заменить 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;
}
Ответ 3
Что вы можете попробовать, это использовать функцию пользовательской сортировки:
sub sort_by_number {
$a =~ /(\d+)/;
$numa = $1;
$b =~ /(\d+)/;
$numb = $1;
return $numa <=> $numb;
}
а затем выполните следующие действия:
@files1 = sort sort_by_number @files;
Это отсортирует строки в @files
по значению первого числа в каждой строке.
Ответ 4
Более старый вопрос, но ответа еще нет.
Sort::Naturally
делает это для вас:
Сортировка лексически, но сортировка числовых частей численно
#!/usr/bin/env perl
use strict;
use warnings;
use Sort::Naturally;
print nsort <DATA>;
__DATA__
fw_d.log
fw_d.log.101.gz
fw_d.log.1.gz
fw_d.log.10.gz
fw_d.log.100.gz
fw_d.log.2.gz
fw_d.log.102.gz
fw_d.log.12.gz
Это означает, что:
fw_d.log
fw_d.log.1.gz
fw_d.log.2.gz
fw_d.log.10.gz
fw_d.log.12.gz
fw_d.log.100.gz
fw_d.log.101.gz
fw_d.log.102.gz