使用Perl创建重复文件名列表

时间:2013-01-21 13:46:23

标签: arrays perl duplicates

我一直在尝试编写一个脚本来预处理一些长文件列表,但我对Perl还没有信心(也没有能力)并且没有得到我想要的结果。

下面的脚本正在进行中,但我仍然坚持检查是否有重复,如果有人能让我知道我哪里出错了,我将不胜感激。处理重复的块似乎与我找到的示例形式相同,但它似乎不起作用。

#!/usr/bin/perl
use strict;
use warnings;

open my $fh, '<', $ARGV[0] or die "can't open: $!";

foreach my $line (<$fh>) {

#   Trim list to remove directories which do not need to be checked
    next if $line =~ m/Inventory/;
#   MORE TO DO 
    next if $line =~ m/Scanned photos/;

    $line =~ s/\n//; # just for a tidy list when testing
    my @split = split(/\/([^\/]+)$/, $line); # separate filename from rest of path

    foreach (@split) {
        push (my @filenames, "$_");
#       print "@filenames\n"; # check content of array

        my %dupes;

        foreach my $item (@filenames) {
            next unless $dupes{$item}++;
            print "$item\n";
        }
    } 
}

我很难理解我的重复检查有什么问题。我知道数组包含重复项(取消注释第一个打印函数给我一个包含大量重复项的列表)。现有代码不会产生任何效果。

不是我帖子的主要目的,但我的最终目的是从列表中删除唯一的文件名,并保留在其他目录中重复的文件名。 我知道这些文件都不相同,但很多是同一文件的不同版本,这就是我专注于文件名的原因。

例如,我想要输入:

〜/图片/ 2010 / 12345678.jpg 〜/图片/ 2010 / 12341234.jpg 〜/桌面/温度/ 12345678.jpg

输出:

〜/图片/ 2010 / 12345678.jpg 〜/桌面/温度/ 12345678.jpg

所以我认为理想情况下,如果可能的话,最好根据正则表达式检查匹配的唯一性而不分裂。

2 个答案:

答案 0 :(得分:5)

以下循环不执行任何操作,因为散列和数组仅包含每个循环迭代的一个值:

foreach (@split) {
    push (my @filenames, "$_");        # add one element to lexical array
    my %dupes;
    foreach my $item (@filenames) {    # loop one time
        next unless $dupes{$item}++;   # add one key to lexical hash
        print "$item\n";
    }
}                                      # @filenames and %dupes goes out of scope

词法变量(用my声明)的范围扩展到周围的块{ ... },在这种情况下是foreach循环。当它们超出范围时,它们将被重置并且所有数据都将丢失。

我不知道为什么要将文件名从@split复制到@filenames,这似乎非常多余。重复数据删除的方法是:

my %seen;
my @uniq;

@uniq = grep !$seen{$_}++, @split;

其他信息:

您可能还有兴趣使用File::Basename获取文件名:

use File::Basename;
my $fullpath = "~/Pictures/2010/12345678.jpg";
my $name = basename($fullpath);                  # 12345678.jpg

您的替换

$line =~ s/\n//;

应该是

chomp($line);

当您从文件句柄中读取时,使用forforeach)表示您已读取所有行并将其存储在内存中。大多数情况下,最好使用while,如下所示:

while (my $line = <$fh>)

答案 1 :(得分:3)

TLP的回答提供了很多好的建议。另外:

为什么同时使用数组和哈希来存储文件名?只需使用哈希作为您的一个存储解决方案,您将自动删除重复项。即:

my %filenames; #outside of the loops

...

foreach (@split) {
    $filenames{$_}++;
}

现在,当您想要获取唯一文件名列表时,只需使用keys %filenames,或者如果您希望按字母顺序使用sort keys %filenames。并且每个哈希键的值是一个出现次数,因此如果您愿意,可以找出哪些是重复的。