3

我一直在尝试编写一个脚本来预处理一些长长的文件列表,但我对 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";
        }
    } 
}

我正在努力理解我的重复检查有什么问题。我知道数组包含重复项(取消注释第一个打印函数会给我一个包含很多重复项的列表)。目前的代码不会产生任何内容。

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

例如,我想要输入:

~/Pictures/2010/12345678.jpg ~/Pictures/2010/12341234.jpg ~/Desktop/temp/12345678.jpg

给出以下输出:

~/Pictures/2010/12345678.jpg ~/Desktop/temp/12345678.jpg

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

4

2 回答 2

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)有一个范围延伸到周围的 block { ... },在这种情况下是你的foreach循环。当它们超出范围时,它们会被重置并且所有数据都会丢失。

我不知道你为什么将文件名从@splitto复制@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);

当您从文件句柄中读取时,使用for( foreach) 意味着您读取所有行并将它们存储在内存中。大多数时候最好改为使用while,如下所示:

while (my $line = <$fh>)
于 2013-01-21T13:57:38.810 回答
3

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

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

my %filenames; #outside of the loops

...

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

现在,当您想要获取唯一文件名列表时,只需使用keys %filenames或,如果您希望它们按字母顺序排列,sort keys %filenames. 每个散列键的值是出现次数,所以如果你关心的话,你可以找出哪些是重复的。

于 2013-01-21T14:22:35.243 回答