【问题标题】:Need help sorting file list based on datestamp in filename需要帮助根据文件名中的日期戳对文件列表进行排序
【发布时间】:2018-03-29 12:18:45
【问题描述】:

未排序的数据

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

预期结果

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

我使用Net::SFTP 从远程站点获取目录列表并与本地文件列表进行比较。我想按文件名中的日期对列表进行排序,但由于字符串中存在我需要忽略的其他信息,我遇到了问题。

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;

我以前做过排序,也做过很多正则表达式,但从来没有同时进行过,而且我显然把它搞砸了,因为它没有对任何东西进行排序,也没有抛出任何错误。

我想过从每个字符串中提取 DD_MM_YYYY_hh_mm_ss 并尝试将其用作参考,但我没有取得任何可用的进展,所以我放弃了这个想法。

【问题讨论】:

    标签: arrays perl sorting hash


    【解决方案1】:

    这会产生您想要的输出。它将下划线或句点上的每一行拆分为一个列表,然后按照您想要的顺序只保留您想要的“列”。它保留年份,然后是月份、日期等。然后它将列表元素连接成一个新的日期字符串,然后根据日期对行进行排序。

    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
    

    我相信您的代码会失败,因为它会按照它们在行上出现的顺序返回列表,即月、日等。

    【讨论】:

    • 比我的解决方案漂亮得多!
    • 谢谢!简单的解决方案,完全按照要求工作。看到你所做的不同突出了为什么我的没有意义,感谢你的帮助。
    【解决方案2】:

    可能不是最漂亮的解决方案,但它有效:

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

    【讨论】:

      【解决方案3】:

      要解析和比较日期,也可以使用日期时间模块,Time::Piece 这里。

      一个简单的版本(见下文更有效的版本)

      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;
      

      这会为每次比较运行一个正则表达式和strptime

      相反,预先计算所有这些

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

      这会提取字符串的日期时间部分,并使用 strptime 从中构建一个日期时间对象,并将其与原始字符串一起放入 arrayref 中。它使用map 对整个输入执行此操作。

      然后将该列表传递给sort,它按其第一个元素对其进行排序,其中使用Time::Piece 对象的内置比较。然后第二个map 将原始字符串拉出,作为我们的结果。

      【讨论】:

        【解决方案4】:

        时间戳与前 9 个字符组合可用作哈希键。

        那么就只是对key和输出数据进行hash排序了。

        use strict;
        use warnings;
        use feature 'say';
        
        my %hash;
        
        while(<DATA>) {
            chomp;
            next unless /(.+?)_(.+?)\.xml/;
            $hash{"$2_$1"} = $_;
        }
        
        say $hash{$_} for sort keys %hash;
        
        __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
        

        输出

        5CM10538_10_15_2017_08_51_06.xml
        NMC00166_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
        

        【讨论】:

          猜你喜欢
          • 2020-04-19
          • 1970-01-01
          • 2015-11-27
          • 1970-01-01
          • 1970-01-01
          • 2018-09-02
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多