كيف يمكنني فرز الملفات إلى الدلائل بناءً على أسماء الملفات؟

StackOverflow https://stackoverflow.com/questions/552512

  •  23-08-2019
  •  | 
  •  

سؤال

لدي عدد كبير من الملفات لفرز جميع الأسماء في بعض الاصطلاحات الرهيبة.
وهنا بعض الأمثلة:

(4)_mr__mcloughlin____.txt
12__sir_john_farr____.txt
(ب)mr__chope____.txt
dame_elaine_kellett-bowman____.txt
dr__بلاكبيرن______.txt

من المفترض أن تكون هذه الأسماء شخصًا مختلفًا (متحدثًا) لكل منهما.قام شخص ما في قسم آخر لتكنولوجيا المعلومات بإنتاج هذه الملفات من عدد كبير من ملفات XML باستخدام بعض البرامج النصية ولكن التسمية غبية بشكل لا يسبر غوره كما ترون.

أحتاج إلى فرز عشرات الآلاف من هذه الملفات حرفيًا باستخدام ملفات نصية متعددة لكل شخص؛يحتوي كل منها على شيء غبي يجعل اسم الملف مختلفًا، سواء كان ذلك بشرطات سفلية أكثر أو بعض الأرقام العشوائية.يجب فرزها حسب المتحدث.

سيكون هذا أسهل باستخدام البرنامج النصي للقيام بمعظم العمل، ثم يمكنني فقط العودة ودمج المجلدات التي يجب أن تكون تحت نفس الاسم أو أي شيء آخر.

هناك عدد من الطرق التي كنت أفكر فيها للقيام بذلك.

  • تحليل الأسماء من كل ملف وفرزها في مجلدات لكل اسم فريد.
  • احصل على قائمة بجميع الأسماء الفريدة من أسماء الملفات، ثم ابحث في هذه القائمة المبسطة للأسماء الفريدة للأسماء المتشابهة واسألني ما إذا كانت متماثلة، وبمجرد تحديد ذلك، سيتم فرزها جميعًا وفقًا لذلك.

أخطط لاستخدام لغة Perl، ولكن يمكنني تجربة لغة جديدة إذا كان الأمر يستحق ذلك.لست متأكدًا من كيفية متابعة قراءة كل اسم ملف في الدليل واحدًا تلو الآخر في سلسلة للتحليل إلى اسم فعلي.لست متأكدًا تمامًا من كيفية التحليل باستخدام regex في Perl أيضًا، ولكن قد يكون ذلك قابلاً للبحث في Google.

بالنسبة للفرز، كنت سأستخدم أمر الصدفة:

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

ولكن فقط لأن هذا كل ما أعرفه لذا فهو أسهل.

ليس لدي حتى فكرة عن الكود الكاذب عما سأفعله أيضًا، لذا إذا كان شخص ما يعرف أفضل تسلسل للإجراءات، فأنا آذان صاغية.أعتقد أنني أبحث عن الكثير من المساعدة، وأنا منفتح على أي اقتراحات.الكثير الكثير شكرا لأي شخص يمكنه المساعدة.

ب.

هل كانت مفيدة؟

المحلول

آمل أن أفهم سؤالك الصحيح، إنه إيمهو غامض بعض الشيء. هذا الرمز مختبر، ولكن يجب أن تفعل ما أعتقد أنك تريد.

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");
    }
}

نصائح أخرى

هل جميع الملفات الحالية في نفس الدليل؟ إذا كان الأمر كذلك، فيمكنك استخدام "opendir" و "readdir" لقراءة جميع الملفات واحدا تلو الآخر. قم ببناء تجزئة باستخدام اسم الملف كمفتاح (إزالة الكل "_" بالإضافة إلى أي معلومات داخل الأقواس) حتى تحصل على شيء من هذا القبيل -

(4)_mr__mcloughlin____.txt -> 'mr mcloughlin'
12__sir_john_farr____.txt -> 'sir john farr'
(b)mr__chope____.txt -> 'mr chope'
dame_elaine_kellett-bowman____.txt -> 'dame elaine kellett-bowman'
dr__blackburn______.txt -> 'dr blackburn'

اضبط قيمة التجزئة على أن يكون عدد مثيلات الاسم حدث حتى الآن. لذلك بعد هذه الإدخالات يجب أن يكون لديك التجزئة التي تبدو وكأنها -

'mr mcloughlin' => 1
'sir john farr' => 1
'mr chope' => 1
'dame elaine kellett-bowman' => 1
'dr blackburn' => 1

كلما أتيت عبر إدخال جديد في جهاز Hash، ما عليك سوى إنشاء دليل جديد باستخدام اسم المفتاح. الآن كل ما عليك فعله هو نسخ الملف باستخدام الاسم الذي تم تغييره (استخدم قيمة التجزئة المقابلة كإحاكي) في الدليل الجديد. لذلك على سبيل المثال، كنت تتعثر على إدخال آخر يقرأ باسم "MCLOUGHLIN"، ثم يمكنك نسخها

./mr mcloughlin/mr mcloughlin_2.txt

أود:

  1. تحديد ما هو مهم في الاسم:

    • يكون dr__blackburn مختلف عن dr_blackburn?
    • يكون dr__blackburn مختلف عن mr__blackburn?
    • هي الأرقام الرائدة ذات معنى؟
    • هي الرائدة / الزائدة تؤكد ذات مغزى؟
    • إلخ.
  2. توصل إلى قواعد وخوارزمية لتحويل اسم إلى دليل (ليون بداية جيدة جدا)

  3. قراءة في الأسماء ومعالجتها واحدة في وقت واحد

    • أود استخدام مزيج من opendir و secursion
    • أود نسخها أثناء تصورها؛ مرة أخرى ليون وظيفة مثال رائع
  4. إذا سيحتاج هذا البرنامج النصي إلى الحفاظ عليه واستخدامه في المستقبل، فسأخلق اختبارات (مثل استخدام http://search.cpan.org/dist/test-more/) لكل مسار Regexp؛ عندما تجد تجعد جديد، أضف اختبارا جديدا وتأكد من فشله، ثم قم بإصلاح Regex، ثم أعد تشغيل الاختبار للتأكد من كسر شيء

لم أستخدم بيرل منذ فترة، لذلك سأكتب هذا في روبي. سأعلق عليه لإنشاء بعض الكود الكفاءة.

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، ولكن لم يتم اختبار هذا الرمز. هل يبدو هذا مثل ما تحاول إنجازه؟ قد يساعدك هذا في كتابة التعليمات البرمجية في بيرل؟

يمكنك تقسيم أسماء الملفات باستخدام شيء مثل

@tokens = split /_+/, $filename

آخر دخول من @tokens يجب ان يكون ".txt" بالنسبة لجميع أسالاك الملف هذه، ولكن يجب أن يكون الشخص الثاني إلى آخر ما يشبه نفس الشخص الذي تم إهمال اسمه في الأماكن (أو "تم تغيير" الدكتور جونز "إلى" براين جونز "على سبيل المثال). قد ترغب في استخدام نوع من تحرير المسافة كما متري التشابه للمقارنة @tokens[-2] لأسماء الملفات المختلفة؛ عندما يكون لدى إدخادين مماثلة بأسماء أخيرة مماثلة، يجب أن يطالبك كمرشح للاندماج.

كما تسأل أ عام جدًا سؤال، أي لغة يمكن أن تفعل ذلك طالما لدينا تدوين أفضل للقواعد.ليس لدينا حتى تفاصيل, ، "عينة" فقط.

لذا، عند العمل بشكل أعمى، يبدو أن هناك حاجة إلى مراقبة بشرية.لذا فإن الفكرة أ غربال.شيء يمكنك تشغيله والتحقق منه بشكل متكرر وتشغيله مرة أخرى والتحقق منه مرارًا وتكرارًا حتى يتم فرز كل شيء إلى بعض المهام اليدوية الصغيرة.

الكود أدناه يجعل الكثير من الافتراضات, ، لأنك تركت الأمر لنا للتعامل معه.أحدها هو أن العينة عبارة عن قائمة بجميع الأسماء الأخيرة المحتملة؛إذا كان هناك أي أسماء عائلة أخرى، قم بإضافتها وتشغيلها مرة أخرى.

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 
                             )
        );
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top