0

恐ろしい慣習で名前が付けられたすべてをソートする膨大な数のファイルがあります。
ここではいくつかの例を示します。

(4)_mr__mcloughlin____.txt
12__sir_john_farr____.txt
(b)mr__chope____.txt
dame_elaine_kellett-bowman____.txt
dr__blackburn______.txt

これらの名前は、それぞれ別の人 (話者) であるはずです。別の IT 部門の誰かが、スクリプトを使用して大量の XML ファイルからこれらを作成しましたが、ご覧のとおり、その命名は計り知れないほどばかげています。

文字通り何万ものこれらのファイルを並べ替え、1 人ごとに複数のテキスト ファイルを並べ替える必要があります。アンダースコアや乱数など、ファイル名を異なるものにする愚かな何かがそれぞれにあります。それらは話者別にソートする必要があります。

これは、スクリプトを使用してほとんどの作業を行う方が簡単で、戻って同じ名前のフォルダーをマージするだけで済みます。

これを行うために私が考えていた方法はいくつかあります。

  • 各ファイルから名前を解析し、一意の名前ごとにフォルダーに並べ替えます。
  • ファイル名からすべての一意の名前のリストを取得し、類似した名前の単純化された一意の名前のリストを調べて、それらが同じかどうかを尋ねます。これを判断すると、それに応じてすべてを並べ替えます。

Perl を使用する予定ですが、価値があれば新しい言語を試すこともできます。ディレクトリ内の各ファイル名を一度に 1 つずつ読み取り、実際の名前に解析する方法がわかりません。perlで正規表現を使用して解析する方法も完全にはわかりませんが、それはグーグル可能かもしれません。

並べ替えには、次のシェル コマンドを使用するつもりでした。

`cp filename.txt /example/destination/filename.txt`

しかし、私が知っているのはそれだけなので、最も簡単です。

私が何をしようとしているのかについての疑似コードのアイデアさえ持っていないので、誰かがアクションの最良のシーケンスを知っているなら、私はすべて耳を傾けます。私は多くの助けを求めていると思います。どんな提案も受け入れます。助けてくれる人に感謝します。

B.

4

6 に答える 6

5

I hope I understand your question right, it's a bit ambiguous IMHO. This code is untested, but should do what I think you want.

use File::Copy;

sub sanatize {
    local $_ = shift;
    s/\b(?:dame|dr|mr|sir)\b|\d+|\(\w+\)|.txt$//g;
    s/[ _]+/ /g;
    s/^ | $//g;
    return lc $_;
}

sub sort_files_to_dirs {
    my @files = @_;
    for my $filename (@files) {
        my $dirname = sanatize($filename);
        mkdir $dirname if not -e $dirname;
        copy($filename, "$dirname/$filename");
    }
}
于 2009-02-16T09:05:44.677 に答える
2

私は...するだろう:

  1. 名前の意味を定義します。

    • とはdr__blackburn異なりdr_blackburnますか?
    • とはdr__blackburn異なりmr__blackburnますか?
    • 先頭の数字は意味がありますか?
    • 先頭/末尾の下線は意味がありますか?
  2. 名前をディレクトリに変換するためのルールとアルゴリズムを考え出す(Leon'sは非常に良いスタートです)

  3. 名前を読み、一度に1つずつ処理します

    • opendirと再帰の組み合わせを使用します
    • あなたがそれらを処理するときに私はそれらをコピーします。再びレオンの投稿は素晴らしい例です
  4. このスクリプトを将来維持して使用する必要がある場合は、正規表現パスごとにテストを確実に作成します(たとえば、 http ://search.cpan.org/dist/Test-More/を使用)。新しいしわを見つけたら、新しいテストを追加して失敗することを確認し、正規表現を修正してから、テストを再実行して何も壊れていないことを確認します

于 2009-02-16T18:54:48.440 に答える
2

しばらくPerlを使っていなかったので、Rubyで書きます。いくつかの疑似コードを確立するためにコメントします。

DESTINATION = '/some/faraway/place/must/exist/and/ideally/be/empty'

# get a list of all .txt files in current directory
Dir["*.txt"].each do |filename|
  # strategy:
  # - chop off the extension
  # - switch to all lowercase
  # - get rid of everything but spaces, dashes, letters, underscores
  # - then swap any run of spaces, dashes, and underscores for a single space
  # - then strip whitespace off front and back
  name = File.basename(filename).downcase.
         gsub(/[^a-z_\s-]+/, '').gsub(/[_\s-]+/, ' ').strip
  target_folder = DESTINATION + '/' + name

  # make sure we dont overwrite a file
  if File.exists?(target_folder) && !File.directory?(target_folder)
    raise "Destination folder is a file"
  # if directory doesnt exist then create it
  elsif !File.exists?(target_folder)
    Dir.mkdir(target_folder)
  end
  # now copy the file
  File.copy(filename, target_folder)
end   

とにかく、それがアイデアです。すべての API 呼び出しが正しいことを確認しましたが、これはテストされたコードではありません。これはあなたが達成しようとしていることのように見えますか? これは Perl でコードを書くのに役立つでしょうか?

于 2009-02-16T07:58:22.420 に答える
1

あなたは非常に一般的な質問をしているので、ルールをより適切に成文化している限り、どの言語でもこれを行うことができます。「サンプル」だけで、詳細さえありません。

したがって、ブラインドで作業すると、人間による監視が必要になるようです。だからアイデアはふるいです。すべてがいくつかの小さな手動タスクに分類されるまで、繰り返し実行して確認して再度実行して何度も確認できるもの。

以下のコードは多くの仮定をしています。その 1 つは、サンプルが可能なすべての姓のリストであることです。他の姓がある場合は、それらを追加して再度実行してください。

use strict;
use warnings;
use File::Copy;
use File::Find::Rule;
use File::Spec;
use Readonly;

Readonly my $SOURCE_ROOT    => '/mess/they/left';
Readonly my $DEST_DIRECTORY => '/where/i/want/all/this';

my @lname_list = qw<mcloughlin farr chope kelette-bowman blackburn>;
my $lname_regex 
    = join( '|'
          , sort {  ( $b =~ /\P{Alpha}/ ) <=> ( $a =~ /\P{Alpha}/ )
                 || ( length $b ) <=> ( length $a ) 
                 || $a cmp $b 
                 } @lname_list 
          )
    ;
my %dest_dir_for;

sub get_dest_directory { 
    my $case = shift;
    my $dest_dir = $dest_dir_for{$case};
    return $dest_dir if $dest_dir;

    $dest_dir = $dest_dir_for{$case}
        = File::Spec->catfile( $DEST_DIRECTORY, $case )
        ;
    unless ( -e $dest_dir ) { 
        mkdir $dest_dir;
    }
    return $dest_dir;
}

foreach my $file_path ( 
    File::Find::Rule->file
        ->name( '*.txt' )->in( $SOURCE_ROOT )
) {
    my $file_name =  [ File::Spec->splitpath( $file_path ) ]->[2];
    $file_name    =~ s/[^\p{Alpha}.-]+/_/g;
    $file_name    =~ s/^_//;
    $file_name    =~ s/_[.]/./;

    my ( $case )  =  $file_name =~ m/(^|_)($lname_regex)[._]/i;

    next unless $case;
    # as we next-ed, we're dealing with only the cases we want here. 

    move( $file_path
        , File::Spec->catfile( get_dest_directory( lc $case )
                             , $file_name 
                             )
        );
}
于 2009-02-16T21:44:52.153 に答える