Frage

Ich habe eine große Anzahl von Dateien alle Namen in irgendeiner schrecklichen Konvention zu sortieren.
Hier sind einige Beispiele:

(4) _mr__mcloughlin ____. Txt
12__sir_john_farr ____. Txt
(B) mr__chope ____. Txt
dame_elaine_kellett-bowman ____. txt
dr__blackburn ______. txt

Diese Namen sollte eine andere Person (Sprecher), die jeweils sein. Jemand in einer anderen IT-Abteilung produzierte diese von einer Tonne von XML-Dateien eines Skript verwenden, aber die Namensgebung ist unergründlich dumm, wie Sie sehen können.

Ich brauche buchstäblich Zehntausende dieser Dateien mit mehreren Dateien von Text für jede Person zu sortieren; jeder mit etwas Dummes machen der Dateiname anders, sei es mehr Unterstrichen oder eine Zufallszahl. Sie müssen von Lautsprecher sortiert werden.

Dies würde mit einem Skript einfacher sein die meiste Arbeit zu tun, dann könnte ich gehen Sie einfach zurück und verschmelzen Ordner, die unter dem gleichen Namen oder was auch immer sein sollte.

Es gibt eine Reihe von Möglichkeiten, wie ich über das tun dies dachte.

  • analysiert die Namen von jeder Datei und sortiert sie in Ordner für jeden eindeutigen Namen.
  • erhält eine Liste aller eindeutigen Namen aus den Dateinamen, dann schauen Sie durch diese vereinfachte Liste von eindeutigen Namen für ähnliche, und mich fragen, ob sie gleich sind, und sobald es diese festgestellt hat, wird sie alle entsprechend sortieren.

Ich plane Perl auf, aber ich kann eine neue Sprache versuchen, wenn es sich lohnt. Ich bin nicht sicher, wie man über in einem Verzeichnis einer nach dem anderen in einen String in jedem Dateinamen zu lesen für in einen tatsächlichen Namen Parsen. Ich bin mir nicht ganz sicher, wie mit Regex in Perl entweder zu analysieren, aber das könnte googleable sein.

Für die Sortierung, ich war nur gonna Verwendung des Shell-Befehl:

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

aber nur dazu führen, das ist alles, was ich weiß, so ist es am einfachsten.

ich nicht sogar eine Pseudo-Code Vorstellung davon, was im entweder so, wenn jemand die beste Folge von Aktionen weiß, ich bin ganz Ohr zu tun gehen. Ich glaube, ich bin auf der Suche nach vieler Hilfe, ich ist für alle Vorschläge offen. Vielen vielen Dank an alle, die helfen können.

B.

War es hilfreich?

Lösung

Ich hoffe, dass ich Ihre Frage richtig verstanden, es ist ein bisschen zweideutig IMHO ist. Dieser Code ist nicht getestet, sollte aber tun, was ich denke, Sie wollen.

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

Andere Tipps

Sind alle aktuellen Dateien im selben Verzeichnis? Wenn das der Fall ist, dann könnte man ‚opendir‘ und ‚readdir‘ verwenden, um alle Dateien nacheinander zu lesen. Erstellen Sie eine Hash den Dateinamen als Schlüssel (entfernen Sie alle ‚_‘ sowie alle Informationen in den Klammern), so dass Sie so etwas wie dieses erhalten -

(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'

Setzen Sie den Wert der Hash die Anzahl der Instanzen des Namens zu sein, so weit kam. Also nach diesen Einträgen sollten Sie einen Hash haben, die so aussieht -

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

Wenn Sie über einen neuen Eintrag in der Hash kommen erstellen Sie einfach ein neues Verzeichnis der Schlüsselnamen verwenden. Alles was Sie jetzt tun müssen, ist die Datei mit dem geänderten Namen kopieren (verwenden Sie den entsprechenden Hash-Wert als Suffix) in das neue Verzeichnis. So zum Beispiel., Von Ihnen waren auf einen anderen Eintrag stolpern, die als ‚mr mcloughlin‘ liest dann könnte man es als

kopieren
./mr mcloughlin/mr mcloughlin_2.txt

Ich würde:

  1. definieren, was im Namen signifikant ist:

    • wird dr__blackburn anders als dr_blackburn?
    • wird dr__blackburn anders als mr__blackburn?
    • sind führende Zahlen sinnvoll?
    • führen / nachlauf unterstreicht sinnvoll?
    • etc.
  2. kommt mit Regeln und einem Algorithmus einen Namen in ein Verzeichnis (Leon ist ein sehr guter Start)

  3. konvertieren
  4. in den Namen lesen und sie einer nach dem anderen verarbeiten

    • würde ich eine Kombination von opendir und Rekursion
    • verwenden
    • Ich würde kopieren Sie sie, wie Sie sie verarbeiten; wieder Leons Beitrag ist ein großartiges Beispiel
  5. , wenn dieses Skript beibehalten und in der Zukunft verwendet werden müssen, würde ich auf jeden Fall Tests erstellen (zB mit http://search.cpan.org/dist/Test-More/ ) für jeden regexp Pfad; wenn Sie eine neue Falten zu finden, einen neuen Test hinzufügen, und stellen Sie sicher, dass es fehlschlägt, dann die Regex beheben, dann den Test erneut ausführen, dass nichts zu machen brach

Ich habe Perl eine Weile nicht benutzt, damit ich diese in Ruby schreiben werde. Ich werde es einen Kommentar zu einig Pseudo-Code einzurichten.

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   

Das ist die Idee, sowieso - ich habe dafür gesorgt, die alle API-Aufrufe korrekt sind, aber dieser Code nicht getestet. Sieht das, was Sie versuchen zu erreichen? Könnte diese Hilfe Sie den Code in Perl schreiben?

Sie können die Dateinamen aufgeteilt etwas wie

mit
@tokens = split /_+/, $filename

Der letzte Eintrag von @tokens für alle diese Dateinamen ".txt" werden soll, aber die zweite bis zum vorletzten sollte für die gleiche Person, deren Name ähnlich ist stellen falsch geschrieben worden ist (oder „Dr. Jones“ auf „Brian Jones " zum Beispiel). Vielleicht möchten Sie eine Art von Editierdistanz rel="nofollow verwenden, wie ein Ahnlichkeitsmetrik @tokens[-2] für verschiedene Dateinamen vergleichen ; wenn zwei Einträge ähnlich genug Nachnamen haben, sie sollten Sie als Kandidat aufgefordert, für die Zusammenführung.

Wie Sie eine fragen sehr allgemein Frage könnte jede Sprache dies tun, solange wir eine bessere Kodifizierung von Regeln. Wir haben nicht einmal die Besonderheiten , nur eine „Probe“.

So arbeitet blind, sieht es aus wie menschliche Überwachung benötigt werden. So ist die Idee, ein Sieb . Etwas, das man immer wieder laufen und überprüfen und erneut ausführen und überprüfen immer wieder, bis Sie alles auf ein paar kleine manuelle Aufgaben sortiert haben.

Der folgende Code macht eine Menge von Annahmen , weil man so ziemlich überlässt es uns, es zu handhaben. Eines davon ist, dass die Probe eine Liste aller möglichen Nachnamen; wenn es irgendwelche anderen Nachnamen sind, fügen Sie ‚em und es wieder laufen.

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 
                             )
        );
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top