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
$stringa
supera 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_string
viene valorizzato come%s%5s%s%s
ovverosprintf "%%s%%%ds%%s%%s", $lunghezza;
converte la stringa passata
come argomento
%%s
viene convertito come%s
, il primo è il separatore di inizio campo, il penultimo di fine campo, l'ultimo è l'evetuale
fine riga;
%%%ds
viene convertito in%$lunghezzas
ovvero%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|%1s
e 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