Il package fmt ha tre famiglie di funzioni di stampa, e ogni famiglia ha le stesse tre varianti:
| Funzione | Dove va l'output | Formato |
|---|---|---|
Print, Println, Printf | standard output | predefinito, predefinito con spazi e a capo, stringa di formato |
Sprint, Sprintln, Sprintf | una string restituita | gli stessi tre stili |
Fprint, Fprintln, Fprintf | qualsiasi io.Writer (file, buffer, risposta HTTP) | gli stessi tre stili |
Errorf | un error restituito | stringa di formato, più %w per il wrapping |
Println aggiunge spazi tra gli operandi e un a capo alla fine. Printf non aggiunge nulla: il \n lo scrivi tu. Print aggiunge spazi solo tra operandi che non sono entrambi stringhe, un comportamento abbastanza sorprendente da spingere la maggior parte del codice a usare Println o Printf.
Verbi di formato
Generali
| Verbo | Stampa | Esempio di output |
|---|---|---|
%v | il valore in un formato predefinito | {Ana 31 [admin]} |
%+v | le struct con i nomi dei campi | {Name:Ana Age:31 Tags:[admin]} |
%#v | la sintassi Go del valore | main.User{Name:"Ana", Age:31, Tags:[]string{"admin"}} |
%T | il tipo | main.User |
%% | un simbolo di percentuale letterale | % |
%+v è quello da usare durante il debug. Un puntatore a una struct viene stampato come &{...} invece che come indirizzo. Le mappe vengono stampate con le chiavi ordinate, quindi l'output è stabile anche se l'ordine di iterazione delle mappe è casuale.
Interi
| Verbo | Significato | fmt.Sprintf(verb, 255) |
|---|---|---|
%d | decimale | 255 |
%b | binario | 11111111 |
%o | ottale | 377 |
%O | ottale con prefisso 0o | 0o377 |
%x / %X | esadecimale, minuscolo o maiuscolo | ff / FF |
%#x | esadecimale con prefisso 0x | 0xff |
%c | il carattere con quel code point | ÿ |
%q | un letterale di carattere tra apici | 'ÿ' |
%U | formato Unicode | U+00FF |
Float
| Verbo | Significato | fmt.Sprintf(verb, 1234.5678) |
|---|---|---|
%f | decimale, 6 cifre dopo la virgola per impostazione predefinita | 1234.567800 |
%.2f | decimale, 2 cifre dopo la virgola | 1234.57 |
%e | notazione scientifica | 1.234568e+03 |
%g | %e o %f, quello più corto, senza zeri finali | 1234.5678 |
%v | come %g | 1234.5678 |
%.2f arrotonda il valore binario esatto del float, quindi fmt.Sprintf("%.2f", 2.675) dà 2.67: il float64 più vicino a 2,675 è leggermente inferiore. Non formattare mai importi di denaro a partire da un float; tieni i centesimi in un intero.
Stringhe e byte
| Verbo | Significato | fmt.Sprintf(verb, "go\n") |
|---|---|---|
%s | la stringa così com'è | go e un a capo |
%q | tra virgolette doppie, con le sequenze di escape visibili | "go\n" |
%x | esadecimale di ogni byte | 676f0a |
% x | esadecimale con spazi | 67 6f 0a |
%s su un []byte lo stampa come testo; %v stampa i numeri ([104 105]).
Altri tipi
| Verbo | Tipo | Stampa |
|---|---|---|
%t | bool | true o false |
%p | puntatore, slice, map, channel, func | l'indirizzo, come 0xc000012345 |
%w | error (solo in Errorf) | il messaggio dell'errore, e lo avvolge |
Larghezza, precisione e riempimento
Tra % e il verbo puoi mettere flag, una larghezza e una precisione:
| Forma | Effetto |
|---|---|
%5d | larghezza 5, allineato a destra (riempie a sinistra con spazi) |
%-5d | larghezza 5, allineato a sinistra |
%05d | riempie con zeri |
%.2f | 2 cifre dopo la virgola |
%8.2f | larghezza 8 e 2 decimali |
%.3s | al massimo 3 caratteri della stringa |
%+d | mostra sempre il segno |
%*d | larghezza presa dall'argomento successivo |
Nota che %.0f di 2.5 stampa 2: qui Go arrotonda la metà al numero pari. Per le stringhe la larghezza conta le rune, non le colonne visualizzate, quindi i caratteri CJK e le emoji possono comunque disallineare una tabella. Per colonne allineate di testo variabile, text/tabwriter fa le misure al posto tuo.
Indici degli argomenti
%[n] sceglie un argomento in base alla posizione, il che ti permette di riusarlo:
fmt.Printf("%[2]s %[1]s\n", "world", "hello") // hello world
fmt.Printf("%d %[1]x %[1]b\n", 10) // 10 a 1010
Errorf e %w
fmt.Errorf costruisce un error a partire da una stringa di formato. Con %w avvolge anche un altro errore, così i chiamanti possono ancora riconoscere l'originale:
Usa %w quando i chiamanti potrebbero dover controllare la causa, e %v quando vuoi nasconderla di proposito. Da Go 1.20 una chiamata a Errorf può contenere più verbi %w. La pagina sulla gestione degli errori approfondisce il wrapping.
Formattazione personalizzata con String()
Qualsiasi tipo con un metodo String() string controlla come lo mostrano %v, %s e Println:
%d scavalca String() e stampa il numero sottostante. Per i tipi di errore il metodo equivalente è Error() string, che ha la precedenza su String().
Quando il verbo è sbagliato
fmt non va mai in panic per un formato errato. Stampa il problema direttamente nel testo:
fmt.Printf("%d\n", "oops")
fmt.Printf("%d %d\n", 1)
fmt.Printf("%d\n", 1, 2)
%!d(string=oops)
1 %!d(MISSING)
1
%!(EXTRA int=2)
Questo output tende ad arrivare in produzione perché non fa crashare nulla. go vet li rileva tutti e tre durante la build:
./main.go:8:2: fmt.Printf format %d has arg "oops" of wrong type string
./main.go:9:2: fmt.Printf format %d reads arg #2, but call has 1 arg
./main.go:10:2: fmt.Printf call needs 1 arg but has 2 args
Note sulle prestazioni
fmt riceve ogni argomento come any ed esamina il suo tipo durante l'esecuzione (ricorrendo alla reflection per struct, slice e mappe), il che va bene per i log e l'output ma si nota nei cicli stretti. Per convertire un singolo numero, strconv.Itoa e strconv.FormatFloat sono più veloci di Sprintf. Per costruire una stringa lunga in un ciclo, scrivi in uno strings.Builder con fmt.Fprintf(&b, ...) invece di concatenare i risultati di Sprintf.
Domande frequenti
Qual è la differenza tra Println, Printf e Sprintf in Go?
fmt.Println stampa i suoi argomenti separati da spazi con un a capo alla fine. fmt.Printf stampa seguendo una stringa di formato e non aggiunge l'a capo. fmt.Sprintf formatta come Printf ma restituisce il risultato come stringa invece di stamparlo. fmt.Errorf fa lo stesso e restituisce un error.
Come stampo una struct con i nomi dei campi in Go?
Usa %+v: fmt.Printf("%+v\n", user) stampa {Name:Ana Age:31}. %v stampa solo i valori, {Ana 31}, e %#v stampa la sintassi Go compreso il tipo, main.User{Name:"Ana", Age:31}.
Come formatto un float con 2 decimali in Go?
Usa %.2f: fmt.Sprintf("%.2f", 3.14159) restituisce "3.14". Aggiungi una larghezza per allineare le colonne, %8.2f, o un segno meno per allineare a sinistra, %-8.2f. strconv.FormatFloat(f, 'f', 2, 64) dà lo stesso risultato senza una stringa di formato.
Cosa fa %w in fmt.Errorf?
%w formatta un errore come %v e in più lo avvolge, così il nuovo errore porta con sé l'originale. errors.Is ed errors.As possono quindi trovare l'errore avvolto: err := fmt.Errorf("load config: %w", os.ErrNotExist) rende vero errors.Is(err, os.ErrNotExist). %w funziona solo in fmt.Errorf.
Perché il mio output mostra %!d(string=...)?
Il verbo non corrisponde al tipo dell'argomento, per esempio %d con una stringa. fmt stampa il problema nel testo invece di andare in panic: %!d(string=oops). Gli argomenti mancanti stampano %!d(MISSING) e quelli in più %!(EXTRA int=2). go vet li rileva tutti e tre prima che tu esegua il programma.