Domanda

Ho un numero enorme di file da ordinare, tutti nominati secondo una terribile convenzione.
Ecco alcuni esempi:

(4)_signor__mcloughlin____.txt
12__sir_john_farr____.txt
(b) signor__chope____.txt
dame_elaine_kellett-bowman____.txt
dr__blackburn______.txt

Si suppone che questi nomi siano ciascuno una persona diversa (oratore).Qualcuno in un altro reparto IT li ha prodotti da una tonnellata di file XML utilizzando alcuni script, ma la denominazione è insondabilmente stupida, come puoi vedere.

Devo ordinare letteralmente decine di migliaia di questi file con più file di testo per ogni persona;ognuno con qualcosa di stupido che rende diverso il nome del file, che si tratti di più caratteri di sottolineatura o di numeri casuali.Devono essere ordinati per parlante.

Sarebbe più semplice con uno script che svolga la maggior parte del lavoro, quindi potrei semplicemente tornare indietro e unire le cartelle che dovrebbero avere lo stesso nome o altro.

Ci sono diversi modi in cui stavo pensando di farlo.

  • analizzare i nomi di ciascun file e ordinarli in cartelle per ciascun nome univoco.
  • ottieni un elenco di tutti i nomi univoci dai nomi dei file, quindi esamina questo elenco semplificato di nomi univoci per quelli simili e chiedimi se sono uguali e, una volta determinato ciò, li ordinerà tutti di conseguenza.

Ho intenzione di usare Perl, ma posso provare un nuovo linguaggio se ne vale la pena.Non sono sicuro di come leggere ciascun nome file in una directory uno alla volta in una stringa per analizzarlo in un nome effettivo.Non sono nemmeno del tutto sicuro di come analizzare le espressioni regolari in Perl, ma potrebbe essere cercabile su Google.

Per l'ordinamento, avrei semplicemente usato il comando shell:

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

ma solo perché è tutto quello che so, quindi è più semplice.

Non ho nemmeno un'idea dello pseudocodice di cosa farò, quindi se qualcuno conosce la migliore sequenza di azioni, sono tutto orecchi.Immagino di aver bisogno di molto aiuto, sono aperto a qualsiasi suggerimento.Molte molte molte grazie a chiunque possa aiutare.

B.

È stato utile?

Soluzione

Spero che ho capito la tua domanda giusta, è un po 'ambigua IMHO. Questo codice non è testato, ma dovrebbe fare quello che penso che si desidera.

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

Altri suggerimenti

Sono tutti i file correnti nella stessa directory? Se questo è il caso, allora si potrebbe usare 'opendir' e 'readdir' per leggere tutti i file uno per uno. Costruire un hash utilizzando il nome del file come chiave (rimuovere tutti i '_' così come qualsiasi informazione all'interno delle parentesi) in modo da ottenere qualcosa di simile -

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

Imposta il valore del hash per essere il numero di istanze del nome si è verificato finora. Così, dopo queste voci si dovrebbe avere un hash che assomiglia a questo -

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

Ogni volta che ci si imbatte in una nuova voce nel tuo hash è sufficiente creare una nuova directory utilizzando il nome della chiave. Ora tutto ciò che dovete fare è copiare il file con il nome modificato (utilizzare il valore hash corrispondente come suffisso) nella nuova directory. Così, per esempio., Di voi dovesse imbattersi in un'altra voce che si legge come 'mr McLoughlin' allora si potrebbe copiare come

./mr mcloughlin/mr mcloughlin_2.txt

Vorrei:

  1. definire cosa è significativo nel nome:

    • È dr__blackburn diverso da dr_blackburn?
    • È dr__blackburn diverso da mr__blackburn?
    • i numeri iniziali hanno un significato?
    • i caratteri di sottolineatura iniziali/finali sono significativi?
    • eccetera.
  2. elaborare regole e un algoritmo per convertire un nome in una directory (quello di Leon è un ottimo inizio)

  3. leggere i nomi ed elaborarli uno alla volta

    • Vorrei utilizzare una combinazione di opendir e ricorsione
    • Li copierei mentre li elabori;ancora una volta il post di Leon è un ottimo esempio
  4. se questo script dovrà essere mantenuto e utilizzato in futuro, creerei sicuramente dei test (ad es.utilizzando http://search.cpan.org/dist/Test-More/) per ogni percorso di espressione regolare;quando trovi una nuova piega, aggiungi un nuovo test e assicurati che fallisca, quindi correggi la regex, quindi esegui nuovamente il test per assicurarti che nulla si sia rotto

non ho usato Perl in un po 'così ho intenzione di scrivere questo in Ruby. Io commento a stabilire qualche pseudocodice.

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   

Questa è l'idea, in ogni caso - ho fatto in modo che tutte le chiamate API sono corrette, ma questo non è testato codice. Ti sembra quello che si sta cercando di realizzare? Questo potrebbe aiutare a scrivere il codice in Perl?

È possibile dividere i nomi dei file utilizzando qualcosa di simile

@tokens = split /_+/, $filename

L'ultima voce di @tokens dovrebbe essere ".txt" per tutti questi nomi di file, ma il secondo per durare dovrebbe essere simile per la stessa persona il cui nome è stato storpiato in luoghi (o "Dr. Jones" cambiato in "Brian Jones " per esempio). Si consiglia di utilizzare una sorta di edit distance come una somiglianza metrica per confrontare @tokens[-2] di vari nomi di file ; quando due voci hanno cognome abbastanza simili, dovrebbero richiedere come candidato per la fusione.

Come si sta chiedendo un molto generale domanda, qualsiasi linguaggio poteva fare questo finché abbiamo una migliore codificazione delle regole. Noi non abbiamo nemmeno i specifiche , solo un "campione".

Così, lavorando cieco, sembra che sarà necessario il monitoraggio umano. Così l'idea è un setaccio . Qualcosa che si può eseguire più volte e verificare ed eseguire nuovamente e verificare ancora e ancora fino a quando hai tutto ordinato ad alcune piccole attività manuali.

Il codice qui sotto marche un sacco di ipotesi , in quanto è praticamente lasciato a noi per gestire la cosa. Uno dei quali è che il campione è una lista di tutti i possibili cognome; se ci sono altri cognomi, aggiungi 'em ed eseguirlo nuovamente.

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 
                             )
        );
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top