2017-10-17 10 views
1

données non triéesBesoin d'aide pour le tri liste des fichiers en Perl basé sur datestamp dans le nom de fichier

5CM00225_10_16_2017_10_54_42.xml 
5CM10538_10_16_2017_11_04_18.xml 
1ZM06004_10_16_2017_11_04_14.xml 
5XM10010_10_17_2017_08_00_47.xml 
5ZM05391_10_15_2017_08_51_07.xml 
5ZM05388_10_17_2017_08_01_06.xml 
5ZM00058_10_17_2017_08_00_49.xml 
NMC00166_10_15_2017_08_51_06.xml 
5CM10538_10_15_2017_08_51_06.xml 

Résultats attendus

NMC00166_10_15_2017_08_51_06.xml 
5CM10538_10_15_2017_08_51_06.xml 
5ZM05391_10_15_2017_08_51_07.xml 
5CM00225_10_16_2017_10_54_42.xml 
1ZM06004_10_16_2017_11_04_14.xml 
5CM10538_10_16_2017_11_04_18.xml 
5XM10010_10_17_2017_08_00_47.xml 
5ZM00058_10_17_2017_08_00_49.xml 
5ZM05388_10_17_2017_08_01_06.xml 

Fondamentalement, je suis Net :: SFTP pour obtenir un répertoire liste hors d'un site distant et comparez à une liste de fichiers locale. Je voudrais trier la liste par date dans le nom de fichier mais en rencontrant des problèmes dus à l'existence d'autres informations dans la chaîne que je dois ignorer.

my $sftp = Net::SFTP->new($host, %args); 

my @list = $sftp->ls($path); 

open(my $fh, '>', $file); # open a log file to save remote directory listing 

    my @sorted = map { $_->[0] } 
     sort { $a->[1] <=> $b->[1] } 
     map { [$_, $_=~/(\d{2})_(\d{2})_(\d{4})_(\d{2})_(\d{2})_(\d{2})/] } # unsuccessful sorting attempt 
     @list; 

    foreach my $item (@sorted) { 
     $i = ${item}->{filename};        
     print $fh "$1\n"; # prints each record to the open log file 
    } 
close $fh; 

Je l'ai fait le tri avant et beaucoup de regex mais jamais en même temps et je suis clairement maladroit, parce que ce n'est pas quelque chose de tri et de ne pas jeter des erreurs.

J'ai pensé à extraire le DD_MM_YYYY_hh_mm_ss de chaque chaîne et en essayant de l'utiliser comme référence, mais je n'ai pas fait de progrès utilisables, donc j'ai gratté l'idée.

Répondre

3

Ceci produit la sortie désirée. Il divise chaque ligne sur un trait de soulignement ou une période en une liste, puis conserve seulement les "colonnes" que vous voulez, dans l'ordre que vous voulez. Il conserve l'année, suivi du mois, du jour, etc. Ensuite, il joint les éléments de la liste dans une nouvelle chaîne de date, puis trie les lignes en fonction des dates.

use warnings; 
use strict; 

my @list; 
while (<DATA>) { 
    chomp; 
    push @list, $_; 
} 

my @sorted = map { $_->[0] } 
    sort { $a->[1] <=> $b->[1] } 
    map { [$_, join '', (split /[_.]/)[3,1,2,4,5,6] ] } 
@list; 

__DATA__ 
5CM00225_10_16_2017_10_54_42.xml 
5CM10538_10_16_2017_11_04_18.xml 
1ZM06004_10_16_2017_11_04_14.xml 
5XM10010_10_17_2017_08_00_47.xml 
5ZM05391_10_15_2017_08_51_07.xml 
5ZM05388_10_17_2017_08_01_06.xml 
5ZM00058_10_17_2017_08_00_49.xml 
NMC00166_10_15_2017_08_51_06.xml 
5CM10538_10_15_2017_08_51_06.xml 

Je crois que votre code échoue car elle retourne la liste dans l'ordre où ils apparaissent sur la ligne, à savoir le mois, le jour, etc.

+1

Beaucoup plus prétentieux que ma solution! – Andrey

+1

Merci! Solution simple, a travaillé exactement comme demandé. Voyant ce que vous avez fait différent a souligné pourquoi le mien n'a pas de sens, appréciez votre aide. – frozenthorn

1

Probablement pas la plus jolie solution mais il fonctionne:

use strict; 
use warnings; 
use Data::Dumper; 

my @list = (
    '5CM00225_10_16_2017_10_54_42.xml', 
    '5CM10538_10_16_2017_11_04_18.xml', 
    '1ZM06004_10_16_2017_11_04_14.xml', 
    '5XM10010_10_17_2017_08_00_47.xml', 
    '5ZM05391_10_15_2017_08_51_07.xml', 
    '5ZM05388_10_17_2017_08_01_06.xml', 
    '5ZM00058_10_17_2017_08_00_49.xml', 
    'NMC00166_10_15_2017_08_51_06.xml', 
    '5CM10538_10_15_2017_08_51_06.xml' 
); 

my @sorted = sort { 
    my ($mm1,$dd1,$yy1,$hh1,$min1,$ss1) = ($a =~ /_(\d{2})_(\d{2})_(\d{4})_(\d{2})_(\d{2})_(\d{2})\.xml$/); 
    my ($mm2,$dd2,$yy2,$hh2,$min2,$ss2) = ($b =~ /_(\d{2})_(\d{2})_(\d{4})_(\d{2})_(\d{2})_(\d{2})\.xml$/); 
    my $x = $yy1.$mm1.$dd1.$hh1.$min1.$ss1; 
    my $y = $yy2.$mm2.$dd2.$hh2.$min2.$ss2; 
    $x <=> $y; 
} @list; 

print Dumper(\@sorted); 
1

Pour analyser et comparer les dates, il est également judicieux d'utiliser un module date-heure, Time::Piece ici.

Une version naïve (voir ci-dessous pour une plus efficace)

use warnings; 
use strict; 
use feature 'say'; 

use Time::Piece; 

my @orig = ( 
    '5CM00225_10_16_2017_10_54_42.xml', 
    '5CM10538_10_16_2017_11_04_18.xml', 
    '1ZM06004_10_16_2017_11_04_14.xml', 
    '5XM10010_10_17_2017_08_00_47.xml', 
    '5ZM05391_10_15_2017_08_51_07.xml', 
    '5ZM05388_10_17_2017_08_01_06.xml', 
    '5ZM00058_10_17_2017_08_00_49.xml', 
    'NMC00166_10_15_2017_08_51_06.xml', 
    '5CM10538_10_15_2017_08_51_06.xml', 
); 

my $dt = Time::Piece->new; 

my @sorted = sort { 
    my $a_dt = $dt->strptime($a =~ /_(.*)\./, '%m_%d_%Y_%H_%M_%S'); 
    my $b_dt = $dt->strptime($b =~ /_(.*)\./, '%m_%d_%Y_%H_%M_%S'); 
    $a_dt <=> $b_dt 
} @orig; 

say for @sorted; 

Cela va un regex et strptime pour chaque comparaison.

Au lieu de cela, les Précalculer tous

my @sorted = 
    map { $_->[1] } 
    sort { $a->[0] <=> $b->[0] } 
    map { [ $dt->strptime(/_(.*)\./, '%m_%d_%Y_%H_%M_%S'), $_ ] } 
    @orig; 

Ceci extrait la partie date-heure de la chaîne et construit une date-heure objet de celui-ci avec strptime, en le plaçant dans un arrayref ainsi que la chaîne d'origine. Il le fait pour toute l'entrée en utilisant map.

Ensuite cette liste est passée à sort qui la trie par son premier élément, où la comparaison interne de l'objet Time::Piece est utilisée. Ensuite, la seconde map tire les cordes d'origine, pour notre résultat.