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

ハッシュと配列にはループの反復ごとに1つの値しか含まれていないため、この以下のループは何もしません。

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>)
于 2013-01-21T13:57:38.810 に答える
3

TLPの答えはたくさんの良いアドバイスを与えてくれます。加えて:

配列とハッシュの両方を使用してファイル名を保存するのはなぜですか?ハッシュを1つのストレージソリューションとして使用するだけで、重複が自動的に削除されます。すなわち:

my %filenames; #outside of the loops

...

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

一意のファイル名のリストを取得するkeys %filenames場合は、またはを使用します。アルファベット順にする場合は、を使用しますsort keys %filenames。また、各ハッシュキーの値は発生回数であるため、気にすると、どのキーが重複していたかを確認できます。

于 2013-01-21T14:22:35.243 に答える