Formattazione dell'output in Java

Cosa si intende con il termine formattare o formattazione?
Semplicemente il dare ad una espressione visualizzata un aspetto più ricco e curato in alcuni dettagli.
Ad esempio, se dobbiamo visualizzare una serie di numeri, possiamo desiderare che essi siano magari disponti in colonna ed allineati a destra, con lo stesso numero di cifre decimali.
Bene, per ottenere un tale risultato, Java mette a disposizione diverse strade, tra cui possiamo citare le istruzioni printf e format.

Le due funzioni sono analoghe e vanno usate attraverso la sintassi che segue:

System.out.format(<testo formattato>, <sequenza argomenti>);

System.out.printf(<testo formattato>, <sequenza argomenti>);
Il testo formattato è una stringa (quindi racchiusa tra virgolette) con parti speciali, dette segnaposto (o placeholder), che, al momento della visualizzazione, sono sostituite, nell'ordine in cui compaiono, dai valori delle corrispondenti variabili o espressioni elencate nella sequenza degli argomenti.
Tali segnaposto devono avere il formato che segue:
  • %b per indicare un valore booleano o una espressione che restituisce una valore booleano;
    %B è equivalemte, ma produce il risultato utilizzando solo caratteri maiuscoli
  • %c per indicare un carattere o una espressione che restituisce un carattere;
    %C è equivalemte, ma produce il risultato utilizzando solo caratteri maiuscoli
  • %s per indicare un testo o una espressione che restituisce una stringa di testo;
    %S è equivalemte, ma produce il risultato utilizzando solo caratteri maiuscoli
  • %d per indicare un numero intero (senza cifre decimali) o una espressione che restituisce un numero intero
  • %f per indicare un numero decimale (con cifre decimali) o una espressione che restituisce un numero decimale
  • %t per indicare una data/ora o una espressione che restituisce una data/ora
  • %n per indicare un ritorno di riga (è equivalente alla sequenza di escape \n)
Cerchiamo di comprenderne meglio l'utilità e l'utilizzo attraverso degli esempi:
// le righe che seguono producono come output la frase
// Ciao mondo da Mario
String nome = "Mario";
System.out.printf("Ciao mondo da %s", nome);

// le righe che seguono producono come output la frase
// Ciao mondo da Mario e da Giuseppe
String nome1 = "Mario";
String nome2 = "Giuseppe";
System.out.printf("Ciao mondo da %s e da %s", nome1, nome2);

// le righe che seguono producono come output la frase
// Ciao mondo da Mario, ho 28 anni
String nome = "Mario";
int eta = 28;
System.out.printf("Ciao mondo da %s, ho %d anni", nome, eta);

// le righe che seguono producono come output la frase
// Ciao mondo da Mario, oggi ho guadagnato 105,523 euro
String nome = "Mario";
float guadagno = 105.523;
System.out.printf("Ciao mondo da %s, oggi ho guadagnato %f euro", nome, guadagno);

// le righe che seguono producono come output la frase
// Ciao mondo da Mario, oggi ho guadagnato 105,523 euro
String nome = "Mario";
float guadagno = 105.523;
System.out.printf("Ciao mondo da %s, oggi ho guadagnato %f euro", nome, guadagno);
Ciascun segnaposto permette anche di specificare informazioni aggiuntive sul numero minimo di caratteri che il dato visualizzato deve occupare, oppure sulla visualizzazione del segno o della quantità di cifre decimali da visualizzare.
Queste informazioni hanno più o meno senso in relazione al tipo di dato a cui sono applicate, ma in teoria, se non viene restituito un errore di compilazione, producono un effetto anche sui dati per i quali non sono prettamente indicati.

Più dettagliatamente, tra il simbolo % e l'indicatore del tipo si possono indicare il segno, la dimensione minima di caratteri da occupare (comprende anche il carattere per il separatore decimale e l'eventuale carattere per il segno) e la precisione, ossia il numero di cifre decimali, utilizzando il formato:

[segno][dimensione].[precisione]

dove:
  • segno è indicato con il carattere + ed indica la volontà di visualizzare il segno sia per i numeri negativi, ma anche per i numeri positivi
  • dimensione specifica, attraverso un numero intero, il numero minimo di caratteri che il dato deve occupare, a partire da destra, comprensivi del separatore decimale e dell'eventuale segno; se il dato è composto da un numero superiore di caratteri, i caratteri eccedenti quelli previsti vengono occupati a destra dei caratteri minimi riservati
  • precisione indica il numero di cifre decimali da visualizzare; il numero viene arrotondato all'ultima cifra decimale visualizzata, completando con degli zeri nel caso le cifre decimali fossero in numero inferiore a quelle specificate
Analizziamoli nello specifico con degli esempi:
/* Per visualizzare, allineando a destra, degli 
importi numerici, in modo occupare 5 caratteri 
per ogni numero ed ottenere l'output che segue:
Canoni affitto locali    : 10000
Spesa energia elettricai :   750
Spesa riscaldamento      :  3250
*/
int canoniAffittoLocali = 10000;
int spesaEnergiaElettrica = 750;
int spesaRiscaldamento = 3250;
System.out.printf("Canoni affitto locali    : %5d%n", canoniAffittoLocali);
System.out.printf("Spesa energia elettricai : %5d%n", spesaEnergiaElettrica);
System.out.printf("Spesa riscaldamento      : %5d%n", spesaRiscaldamento);


/* Per visualizzare, allineando a destra, degli importi numerici con 
un certo numero di cifre decimali in modo occupare 8 caratteri per ogni
numero, con 2 cifre decimali, in modo da ottenere l'output che segue:
Canoni affitto locali    : 10000.96
Spesa energia elettricai :   750.35
Spesa riscaldamento      :  3250.73
*/
float canoniAffittoLocali = 10000.9622;
float spesaEnergiaElettrica = 750.346997;
float spesaRiscaldamento = 3250.7296745;
System.out.printf("Canoni affitto locali    : %8.2f%n", canoniAffittoLocali);
System.out.printf("Spesa energia elettricai : %8.2f%n", spesaEnergiaElettrica);
System.out.printf("Spesa riscaldamento      : %8.2f%n", spesaRiscaldamento);


/* Per visualizzare, allineando a destra, degli importi numerici con 
un certo numero di cifre decimali in modo occupare 6 caratteri per ogni
numero, con segno e 2 cifre decimali, in modo da ottenere l'output che segue:
Temperatura ore 13   : +12.34
Temperatura ore 14   :  -4.22
Temperatura ore 15   :  +2.63
*/
float tempOre13 = 12.3421;
float tempOre14 = -4.218965557;
float tempOre15 = 2.631289754;
System.out.printf("Temperatura ore 13   : %+6.2f%n", tempOre13);
System.out.printf("Temperatura ore 14   : %+6.2f%n", tempOre14);
System.out.printf("Temperatura ore 15   : %+6.2f%n", tempOre15);