2012-06-09 13 views
6

Sto lavorando su un programma che richiede l'input dell'utente per due nomi di file. Sfortunatamente, il programma può facilmente interrompersi se l'utente non segue il formato specificato dell'input. Voglio scrivere codice che migliori la sua resilienza contro questi tipi di errori. Capirai quando si vede il mio codice:Regex: come rimuovere spazi aggiuntivi tra le stringhe in Perl

# Ask the user for the filename of the qseq file and barcode.txt file 
print "Please enter the name of the qseq file and the barcode file separated by a comma:"; 
# user should enter filenames like this: sample1.qseq, barcode.txt 

# remove the newline from the qseq filename 
chomp ($filenames = <STDIN>); 

# an empty array 
my @filenames; 

# remove the ',' and put the files into an array separated by spaces; indexes the files 
push @filename, join(' ', split(',', $filenames)) 

# the qseq file 
my $qseq_filename = shift @filenames; 

# the barcode file. 
my barcode = shift @filenames; 

Ovviamente questo codice viene eseguito possono incorrere in errori se l'utente inserisce il tipo sbagliato di nome di file (file .tab anziché .txt o .seq invece di .qseq) . Voglio il codice che può fare una sorta di controllo per vedere che l'utente inserisce il tipo di file appropriato.

Un altro errore che potrebbe interrompere il codice è se l'utente immette troppi spazi prima dei nomi file. Ad esempio: sample1.qseq, (immagina 6 spazi qui) barcode.txt (noti i numerosi spazi dopo la virgola)

Un altro esempio: (immagina 6 spazi qui) sample1.qseq, barcode.txt (questa volta si noti il numero di spazi prima del primo nome file)

Desidero anche linee di codice in grado di rimuovere gli spazi aggiuntivi in ​​modo che il programma non si interrompa. Penso che l'input dell'utente debba essere nel seguente tipo di formato: sample1.qseq, barcode.txt. L'input dell'utente deve essere in questo formato in modo da poter indicizzare correttamente i nomi dei file in una matrice e spostarli in un secondo momento.

Grazie a qualsiasi aiuto o suggerimento sono molto apprezzati!

+0

Ho dimenticato di dire: Questo è solo uno dei sei script devo modificare per una corsa in pipe nella riga di comando. In altre parole, voglio che la corsa in pipe funzioni come: Script00.pl | Script01.pl | Script02.pl | Script03.pl | Script04.pl | Script05.pl | Script06.pl. Questo è il primo script in pipe – cooldood3490

risposta

8

Il metodo standard per affrontare questo tipo di problema è l'utilizzo opzioni della riga di comando, non raccogliendo l'input da STDIN. Getopt::Long viene fornito con Perl ed è servicable:

use strict; use warnings FATAL => 'all'; 
use Getopt::Long qw(GetOptions); 
my %opt; 
GetOptions(\%opt, 'qseq=s', 'barcode=s') or die; 
die <<"USAGE" unless exists $opt{qseq} and $opt{qseq} =~ /^sample\d[.]qseq$/ and exists $opt{barcode} and $opt{barcode} =~ /^barcode.*\.txt$/; 
Usage: $0 --qseq sample1.qseq --barcode barcode.txt 
     $0 -q sample1.qseq -b barcode.txt 
USAGE 
printf "q==<%s> b==<%s>\n", $opt{qseq}, $opt{barcode}; 

La shell si occuperà di qualsiasi spazio bianco estranei, provare e vedere. Devi fare la validazione dei nomi dei file, ho inventato qualcosa con regex nell'esempio. Impiegare Pod::Usage per un modo più elaborato di produrre documentazione utile per gli utenti che rischiano di ottenere l'invocazione errata.

Ci sono dozzine di moduli Getopt più avanzati su CPAN.

+0

grazie daxim! sembra come utilizzare le opzioni della riga di comando con Getopt :: Long è la strada da percorrere. Inoltre sembra che tu fornisca anche un assegno per verificare che il nome del file sia corretto. grazie, non avrei capito da solo. Puoi spiegare rapidamente come funziona ogni riga del codice? Con * quasi * un anno di esperienza, sono ancora un programmatore Perl relativamente novizio. Vedo che memorizzi i nomi dei file in un hash% opt. Ma puoi spiegare come funziona il bit di regex e l'USAGE e le altre parti funzionano? Guarderò il modulo Getopt :: Long. – cooldood3490

+0

Inoltre, pensi che questo modulo funzionerà per il tipo di progetto generale su cui sto lavorando? Vedete, questo è solo uno dei sei script che devo modificare per una esecuzione in pipe nella riga di comando. In altre parole, voglio che la corsa in pipe funzioni come: Script00.pl | Script01.pl | Script02.pl | Script03.pl | Script04.pl | Script05.pl | Script06.pl. qualsiasi feedback di follow-up è molto apprezzato – cooldood3490

+0

I comandi di piping funzionano interamente in base al loro output. Fondamentalmente l'output del primo comando deve essere quello che ti serve come input per il comando successivo. – Ilion

4

Innanzitutto, inserire use strict; nella parte superiore del codice e dichiarare le variabili.

In secondo luogo, questo:

# remove the ',' and put the files into an array separated by spaces; indexes the files 
push @filename, join(' ', split(',', $filenames)) 

non ha intenzione di fare quello che vuoi. split() prende una stringa e la trasforma in una matrice. Join prende un elenco di elementi e restituisce una stringa. Devi solo dividerlo:

my @filenames = split(',', $filenames); 

Questo creerà un array come ti aspetti.

Questa funzione tagliare in modo sicuro lo spazio bianco dalla all'inizio e alla fine di una stringa:

sub trim { 
    my $string = shift; 
    $string =~ s/^\s+//; 
    $string =~ s/\s+$//; 
    return $string; 
} 

accesso in questo modo:

my $file = trim(shift @filenames); 

A seconda del vostro scritto, potrebbe essere più facile far passare le stringhe come argomenti della riga di comando. È possibile accedere attraverso l'array @ARGV ma io preferisco usare Getopt :: Long:

use strict; 
use Getopt::Long; 
Getopt::Long::Configure("bundling"); 

my ($qseq_filename, $barcode); 

GetOptions (
    'q|qseq=s' => \$qseq_filename, 
    'b|bar=s' => \$barcode, 
); 

È quindi possibile chiamare questo come:

./script.pl -q sample1.qseq -b barcode.txt 

e le variabili saranno adeguatamente popolato, senza la necessità preoccuparsi di tagliare lo spazio bianco.

+0

grazie a Llion per la revisione del mio codice. Potrei usare la subroutine di trim che hai fornito. Quello dovrebbe occuparsi di qualsiasi spazio bianco iniziale o finale. il modulo GetOpt :: Long che hai suggerito suona come la cosa di cui ho bisogno, questo è solo un frammento del progetto generale. Vedete, questo è solo uno dei sei script che devo modificare per una esecuzione in pipe nella riga di comando. In altre parole, voglio che la corsa in pipe funzioni come: Script00.pl | Script01.pl | Script02.pl | Script03.pl | Script04.pl | Script05.pl | Script06.pl. Sicuramente vedrò se questo modulo funziona bene per quello. Grazie ancora – cooldood3490

1

Mentre penso che il tuo design sia un po 'incerto, quanto segue funzionerà?

my @fileNames = split(',', $filenames); 
foreach my $fileName (@fileNames) { 
    if($fileName =~ /\s/) { 
    print STDERR "Invalid filename."; 
    exit -1; 
    } 
} 
my ($qsec, $barcode) = @fileNames; 
+0

Questo però non risponde alla domanda. Si sbaglia solo quando il formato è inaspettato. Cosa succede se ci sono spazi nel nome del file? – Ilion

+0

sì, immagino che qualcosa del genere possa rendere l'utente rapidamente frustrato. Sto cercando di scrivere codice che sia user-friendly. buon suggerimento però. – cooldood3490

1

E qui è un altro modo si potrebbe farlo con regex (se state leggendo l'input da STDIN):

# read a line from STDIN 
my $filenames = <STDIN>; 

# parse the line with a regex or die with an error message 
my ($qseq_filename, $barcode) = $filenames =~ /^\s*(\S.*?)\s*,\s*(\S.*?)\s*$/ 
    or die "invalid input '$filenames'"; 
Problemi correlati