重複ファイルの整理

写真を整理していて、重複したファイルがたくさんあることに気付いた。最初はWindowsのエクスプローラでサイズ順にならべて、目で写真をみながら消していたのだが大量にありすぎるのでperlでなんとかしようと思った。まず、あるディレクトリ以下のjpgファイルとそのサイズを取得する。(thanks to internet)

recursive( '.' );
exit();

sub recursive
{
    my( $sBaseDir ) = @_;
    my( @FileLists, $sFileName );

    @FileLists = glob( $sBaseDir.'/*' );

    foreach $sFileName ( sort( @FileLists ) ){
        if ( -d $sFileName ){
            &recursive( $sFileName );
        } elsif ( -f $sFileName ){
            if ($sFileName =~ /\.jpg/){
                $filesize = -s $sFileName;
                print( "$filesize $sFileName \n" );
            }
        }
    }
}
あと、これをしないとフォルダやファイル名に空白が含まれたときにうまくいかない。
ppm install File-Glob-Windows
これでサイズとファイル名が取れる。今回はこれをExcelで開いてサイズでソートしてサイズが同じものだけを残した後、秀丸で置換を使ってhtmlにし、それを見て不要なファイルを残して、また秀丸の置換を使ってバッチファイルを作って消した。サイズが同じjpgでももちろん違うファイルの場合がある。ここは目視で確認せざるを得ない。だが、サイズが同じもののみを表示するhtmlファイルを作るまでなら一発で作れるだろう。

perlにはsortという関数があって1次元の配列なら簡単にソートできるが多次元になるとちょっと面倒だ。WEBにサンプルがいくつかあるのでそれを使えばいいのだが・・・と思ったところで気付いた。別に大きさ順にソートする必要はない。文字列としてソートしても重複なら検知できる。

my @found_files;

sub recursive
{
    my( $sBaseDir ) = @_;
    my( @FileLists, $sFileName );

    @FileLists = glob( $sBaseDir.'/*' );

    foreach $sFileName ( sort( @FileLists ) ){
        if ( -d $sFileName ){
            &recursive( $sFileName );
        } elsif ( -f $sFileName ){
            if ($sFileName =~ /\.jpg/){
                $filesize = -s $sFileName;
                $tmp = $filesize." ".$sFileName;
                push(@found_files,$tmp);
            }
        }
    }
}

recursive( '.' );

@found_files = sort(@found_files);

@saved_files=();
$saved_size=0;

foreach(@found_files){
    @a = split(/ /,$_);
    if(@a[0] ne $saved_size){
        if($#saved_files>0){
            foreach $b(@saved_files){
                $b =~ s/^/<img src="/g;
                $b =~ s/$/">/g;
                print "$b\n";
            }
            print "<br>";
        }
        @saved_files=();
        push(@saved_files,@a[1]);
        $saved_size=@a[0];
    }else{
        push(@saved_files,@a[1]);
    }
}
exit();

サイズだけでは結構同じものがある。ファイルサイズだけでなく、確か widthとheightも取れるはず。それも見よう。

use Image::Size;

my @found_files;

sub recursive
{
    my( $sBaseDir ) = @_;
    my( @FileLists, $sFileName );

    @FileLists = glob( $sBaseDir.'/*' );

    foreach $sFileName ( sort( @FileLists ) ){
        if ( -d $sFileName ){
            &recursive( $sFileName );
        } elsif ( -f $sFileName ){
            if ($sFileName =~ /\.jpg/){
                $filesize = -s $sFileName;
                $tmp = $filesize." ".$sFileName;
                push(@found_files,$tmp);
            }
        }
    }
}

recursive( '.' );

@found_files = sort(@found_files);

@saved_files=();
$saved_size=0;
$saved_width=0;
$saved_height=0;

foreach(@found_files){
    @a = split(/ /,$_);
    ($new_width,$new_height) = imgsize(@a[1]);
    if(@a[0] ne $saved_size){
        if($#saved_files>0){
            foreach $b(@saved_files){
                $c =$b;
                $b =~ s/^/<img src="/g;
                $b =~ s/$/">/g;
                print "$c$b\n";
            }
        print "<br>";
    }
    @saved_files=();
    push(@saved_files,@a[1]);
    $saved_size=@a[0];
    $saved_width=$new_width;
    $saved_height=$new_height;
    }else{
        if($new_width == $saved_width){
            push(@saved_files,@a[1]);
        }
    }
}
exit();


Image::Size モジュールを使って、widthもチェックした。これくらいできれば、個人の使用としては十分。