Ecco un altro esempio, questa volta in italiano, di uso infantile di Perl e in generale scorretto della formattazione di stringhe.
L'idea è semplice: occorre stampare in un file a formato fisso delle linee di valori. Si noti bene che il formato fisso si presta bene all'uso della
printf, e sicuramente all'uso dei formati Perl, anche se con campi molto lunghi questi diventano scomodi e francamente poco leggibili.
Ma l'ignoranza e l'inesperienza facevano da padroni nei primi anni della programmazione, così seguendo il fantastico principio del reinventare la ruota ecco che si implementava una sorta di sprintf del poveraccio:
my $INIZIO_CAMPO = "|";
my $FINE_CAMPO = "|";
sub formatta($$$){
my ($stringa, $lunghezza, $fineRiga) = @_;
$stringa =~ s/,/./g;
if( length($stringa) > $lunghezza ){
$stringa = substr($stringa, 0, $lunghezza);
}
if( $fineRiga != 1 ){
return $INIZIO_CAMPO . $stringa . $FINE_CAMPO . $SEPARATORE_CAMPO;
} else {
return $INIZIO_CAMPO . $stringa . $FINE_CAMPO . $FINE_LINEA;
}
}
Piuttosto elementare:
- si sostituisce il carattere , con il
., e questo ha poco a che fare con la stampa stessa;
- se la stringa di partenza
$stringasupera la lunghezza del campo (specificata in$lunghezza)
si tronca la stringa (mentre non c'è un padding qualora la stringa sia piu' corta);
- si concatena la stringa con i delimitatori di campo e si aggiunge, eventualmente, un carattere di
fine linea.
Come si potrebbe fare una versione compatta? O meglio, come affronterei il problema oggi?
Beh, come già detto,
Beh, come già detto,
printf(3p) è la salvezza:sub formatta{
my ( $stringa, $lunghezza, $fine_riga ) = @_;
my $format_string = sprintf "%%s%%%ds%%s%%s", $lunghezza;
return sprintf $format_string, $INIZIO_CAMPO, $stringa, $FINE_CAMPO, ( $fine_riga ? "\n" : "" );
}
L'unica complicazione evidente è la gestione della lunghezza della stringa, che deve essere specificata nella stringa di formato.
Si supponga di chiamare la funzione
Si supponga di chiamare la funzione
formatta come segue:say formatta 'foo', 5, 1;
Ora quello che succede è che:
$format_stringviene valorizzato come%s%5s%s%sovverosprintf "%%s%%%ds%%s%%s", $lunghezza;converte la stringa passata
come argomento
%%sviene convertito come%s, il primo è il separatore di inizio campo, il penultimo di fine campo, l'ultimo è l'evetuale
fine riga;
%%%dsviene convertito in%$lunghezzasovvero%5s;
- la stringa composta in
$format_stringè ancora una stringa valida per laprintf, che quindi viene riempita con
i valori appositi.
Sicuramente questa versione è piu' compatta e piu' manutenibile, a patto di leggere correttamente il formato della
costruito nel primo passaggio (es.
pritnf e il "doppio formato"costruito nel primo passaggio (es.
%%s).
E' anche possibile compattare ancora di piu' cecando di evitare i doppi passaggi, o meglio di limitarli al massimo:
sub formatta{
my ( $stringa, $lunghezza, $fine_riga ) = @_;
my $format_string = sprintf "%1s%%%ds%1s%%1s", $INIZIO_CAMPO, $lunghezza, $FINE_CAMPO;
return sprintf $format_string, $stringa, ( $fine_riga ? "\n" : "" );
}
Il concetto rimane il medesimo, ma la
e quindi richiede solo due parametri (la stringa e il fine riga).
$format_string in uscita dalla prima invocazione di sprintf vale qualcosa come |%5s|%1se quindi richiede solo due parametri (la stringa e il fine riga).
E' poi possibile allineare la stringa a sinistra (invece che a destra) semplicemente usando un padding negativo (es
-5%s).
La lezione imparata è dunque: la
printf è molto versatile, e nella maggior parte dei casi può risolvere parecchi grattacapi di formattazione!
Nessun commento:
Posta un commento