【问题标题】:How to filter down a list of files to remove known duplicates如何过滤文件列表以删除已知重复项
【发布时间】:2017-08-11 17:20:13
【问题描述】:

我有以下文件列表: INV_1400524_20170412_052945.pdf INV_1400524_20170412_063522.pdf INV_1400524_20170412_090338.pdf INV_1400524_20170412_092911.pdf INV_1400971_20170502_095250.pdf INV_1401580_20170703_100410.pdf INV_1401880_20170804_112917.pdf RIN_1300355_20170503_014347.pdf RIN_1300552_20170518_111143.pdf RIN_1300552_20170518_122055.pdf RIN_1300688_20170627_040340.pdf RIN_1300834_20170727_113641.pdf RIN_1300834_20170727_154404.pdf

格式如下:

<Document Type>_<Document Number>_<Date>_<Time>.pdf

如您所见,由于某种原因,同一个文档编号已被多次输出。我想忽略重复项并将列表过滤为唯一的文档编号和最新日期。如果有帮助,这些文档还有一个修改过的文件时间戳,它与文件名中的日期和时间非常匹配。

使用 perl(我一直在使用 File::Find::Rule)我想将列表缩减为: INV_1400524_20170412_092911.pdf INV_1400971_20170502_095250.pdf INV_1401580_20170703_100410.pdf INV_1401880_20170804_112917.pdf RIN_1300355_20170503_014347.pdf RIN_1300552_20170518_122055.pdf RIN_1300688_20170627_040340.pdf RIN_1300834_20170727_154404.pdf

我已经开始了

my @pdf_files = File::Find::Rule->new
  ->in($root_dir)
   ->name( '*.pdf' )
   ->mtime (">$days_ago");

但是看看这个答案: How can I find the newest .pl file in a directory and all its subdirectories using Perl?

我认为可能有一种使用方法:

my $rule = File::Find::Rule->new;
$rule->or( $rule->new->name('INV_*.pdf')->....
$rule->or( $rule->new->name('RIN_*.pdf')->....
my @files = $rule->in($root_dir);

对它们进行分组和过滤。有什么想法吗?

【问题讨论】:

    标签: perl duplicates


    【解决方案1】:

    grep 有一个很好的习惯用法:

    my %seen; 
    my @files = grep { not $seen{$_}++ } @files;
    

    因为你是后增量的,所以第一次测试为真,其他所有测试为假。您还可以使用正则表达式在例如上进行子字符串匹配文档 ID:

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    use Data::Dumper;
    
    chomp(
       my @files = <DATA>
    );
    
    my %seen;
    @files = grep { m/(\d+)/ and not $seen{$1}++ } @files;
    
    print Dumper \@files;
    
    __DATA__
    INV_1400524_20170412_052945.pdf
    INV_1400524_20170412_063522.pdf
    INV_1400524_20170412_090338.pdf
    INV_1400524_20170412_092911.pdf
    INV_1400971_20170502_095250.pdf
    INV_1401580_20170703_100410.pdf
    INV_1401880_20170804_112917.pdf
    RIN_1300355_20170503_014347.pdf
    RIN_1300552_20170518_111143.pdf
    RIN_1300552_20170518_122055.pdf
    RIN_1300688_20170627_040340.pdf
    RIN_1300834_20170727_113641.pdf
    RIN_1300834_20170727_154404.pdf
    

    这个输出:

    $VAR1 = [
              'INV_1400524_20170412_052945.pdf',
              'INV_1400971_20170502_095250.pdf',
              'INV_1401580_20170703_100410.pdf',
              'INV_1401880_20170804_112917.pdf',
              'RIN_1300355_20170503_014347.pdf',
              'RIN_1300552_20170518_111143.pdf',
              'RIN_1300688_20170627_040340.pdf',
              'RIN_1300834_20170727_113641.pdf'
            ];
    

    如果您的条件更符合要求,那么您可能需要应用排序以确保将“第一个”过滤到顶部。

    那里有两种方法 - 你可以在文件名上sort - 因为你有一个 ISO 日期,它看起来会起作用:

    @files = grep { m/(\d+)/ and not $seen{$1}++ } sort @files;
    

    或者您可以基于进行stat 系统调用进行某种处理(不过,您需要完整的文件路径,因此请注意!)

    @files = grep { m/(\d+)/ and not $seen{$1}++} sort { -M $a <=> -M $b } @files;
    

    -M 是检查文件年龄(以天为单位)的 perl 文件测试。

    您可以改用stat

    【讨论】:

    • 谢谢 - 会试试这个。正如您在上面指出的,我正在寻找最新的日期而不是最早的日期。
    • 您可能需要在排序条件中交换$a$b。但仅此而已。
    • 使用@files = grep { m/(\d+)/ and not $seen{$1}++ } reverse sort @files;确实有效
    猜你喜欢
    • 1970-01-01
    • 2015-01-06
    • 2019-04-25
    • 2017-06-09
    • 1970-01-01
    • 2019-11-20
    • 2010-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多