Pakiet fmt ma trzy rodziny funkcji wypisujących, a każda rodzina ma te same trzy warianty:
| Funkcja | Dokąd trafia wynik | Format |
|---|---|---|
Print, Println, Printf | standardowe wyjście | domyślny, domyślny ze spacjami i nową linią, string formatujący |
Sprint, Sprintln, Sprintf | zwracany string | te same trzy style |
Fprint, Fprintln, Fprintf | dowolny io.Writer (plik, bufor, odpowiedź HTTP) | te same trzy style |
Errorf | zwracany error | string formatujący plus %w do opakowywania |
Println dodaje spacje między argumentami i nową linię na końcu. Printf nic nie dodaje: \n dopisujesz samodzielnie. Print dodaje spacje tylko między argumentami, z których żaden nie jest stringiem, co jest na tyle zaskakujące, że większość kodu używa Println lub Printf.
Czasowniki formatujące
Ogólne
| Czasownik | Wypisuje | Przykładowy wynik |
|---|---|---|
%v | wartość w domyślnym formacie | {Ana 31 [admin]} |
%+v | struktury z nazwami pól | {Name:Ana Age:31 Tags:[admin]} |
%#v | wartość w składni Go | main.User{Name:"Ana", Age:31, Tags:[]string{"admin"}} |
%T | typ | main.User |
%% | dosłowny znak procentu | % |
%+v to czasownik, po który warto sięgać przy debugowaniu. Wskaźnik na strukturę wypisuje się jako &{...}, a nie jako adres. Mapy wypisują się z posortowanymi kluczami, więc wynik jest stabilny, choć kolejność iteracji po mapie jest losowa.
Liczby całkowite
| Czasownik | Znaczenie | fmt.Sprintf(verb, 255) |
|---|---|---|
%d | dziesiętnie | 255 |
%b | binarnie | 11111111 |
%o | ósemkowo | 377 |
%O | ósemkowo z prefiksem 0o | 0o377 |
%x / %X | szesnastkowo, małe lub wielkie litery | ff / FF |
%#x | szesnastkowo z prefiksem 0x | 0xff |
%c | znak o tym punkcie kodowym | ÿ |
%q | literał znakowy w apostrofach | 'ÿ' |
%U | format Unicode | U+00FF |
Liczby zmiennoprzecinkowe
| Czasownik | Znaczenie | fmt.Sprintf(verb, 1234.5678) |
|---|---|---|
%f | dziesiętnie, domyślnie 6 miejsc | 1234.567800 |
%.2f | dziesiętnie, 2 miejsca | 1234.57 |
%e | notacja naukowa | 1.234568e+03 |
%g | %e lub %f, co krótsze, bez końcowych zer | 1234.5678 |
%v | to samo co %g | 1234.5678 |
%.2f zaokrągla dokładną binarną wartość liczby, więc fmt.Sprintf("%.2f", 2.675) daje 2.67: najbliższy float64 dla 2.675 jest odrobinę mniejszy. Nigdy nie formatuj kwot pieniężnych z floata; trzymaj grosze w liczbie całkowitej.
Stringi i bajty
| Czasownik | Znaczenie | fmt.Sprintf(verb, "go\n") |
|---|---|---|
%s | zwykły string | go i znak nowej linii |
%q | w cudzysłowach, z widocznymi sekwencjami ucieczki | "go\n" |
%x | szesnastkowo każdy bajt | 676f0a |
% x | szesnastkowo ze spacjami | 67 6f 0a |
%s na []byte wypisuje go jako tekst; %v wypisuje liczby ([104 105]).
Inne typy
| Czasownik | Typ | Wypisuje |
|---|---|---|
%t | bool | true lub false |
%p | wskaźnik, slice, mapa, kanał, funkcja | adres, na przykład 0xc000012345 |
%w | error (tylko w Errorf) | komunikat błędu, a do tego go opakowuje |
Szerokość, precyzja i dopełnianie
Między % a czasownikiem możesz wstawić flagi, szerokość i precyzję:
| Forma | Efekt |
|---|---|
%5d | szerokość 5, wyrównanie do prawej (dopełnienie spacjami z lewej) |
%-5d | szerokość 5, wyrównanie do lewej |
%05d | dopełnienie zerami |
%.2f | 2 cyfry po kropce dziesiętnej |
%8.2f | szerokość 8 i 2 miejsca po przecinku |
%.3s | najwyżej 3 znaki stringa |
%+d | zawsze pokazuj znak |
%*d | szerokość brana z następnego argumentu |
Uwaga: %.0f dla 2.5 wypisuje 2: Go zaokrągla tu połówki do parzystej. Dla stringów szerokość liczy runy, a nie kolumny na ekranie, więc znaki CJK i emoji mogą nadal rozjechać tabelę. Do wyrównanych kolumn ze zmiennym tekstem użyj text/tabwriter, który zmierzy wszystko za ciebie.
Indeksy argumentów
%[n] wybiera argument według pozycji, co pozwala użyć go ponownie:
fmt.Printf("%[2]s %[1]s\n", "world", "hello") // hello world
fmt.Printf("%d %[1]x %[1]b\n", 10) // 10 a 1010
Errorf i %w
fmt.Errorf buduje error ze stringa formatującego. Z %w dodatkowo opakowuje inny błąd, więc wywołujący nadal mogą wykryć oryginał:
Używaj %w, gdy wywołujący mogą potrzebować sprawdzić przyczynę, a %v, gdy celowo ją ukrywasz. Od Go 1.20 jedno wywołanie Errorf może zawierać kilka czasowników %w. Opakowywanie szczegółowo omawia strona o obsłudze błędów.
Własne formatowanie przez String()
Każdy typ z metodą String() string decyduje, jak pokazują go %v, %s i Println:
%d omija String() i wypisuje liczbę bazową. Dla typów błędów odpowiednikiem jest metoda Error() string, która ma pierwszeństwo przed String().
Gdy czasownik jest zły
fmt nigdy nie wywołuje panic przy złym formacie. Wypisuje problem w miejscu:
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)
Taki wynik często trafia na produkcję, bo niczego nie wywraca. go vet wyłapuje wszystkie trzy przypadki podczas budowania:
./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
Uwagi o wydajności
fmt przyjmuje każdy argument jako any i sprawdza jego typ w czasie działania (dla struktur, slice'ów i map korzystając z refleksji), co jest w porządku przy logowaniu i wypisywaniu, ale mierzalne w ciasnych pętlach. Do konwersji pojedynczej liczby strconv.Itoa i strconv.FormatFloat są szybsze niż Sprintf. Do budowania długiego stringa w pętli zapisuj do strings.Builder przez fmt.Fprintf(&b, ...) zamiast łączyć wyniki Sprintf.
Najczęściej zadawane pytania
Czym różnią się Println, Printf i Sprintf w Go?
fmt.Println wypisuje argumenty rozdzielone spacjami, ze znakiem nowej linii na końcu. fmt.Printf wypisuje według stringa formatującego i nie dodaje nowej linii. fmt.Sprintf formatuje tak samo jak Printf, ale zamiast wypisywać, zwraca wynik jako string. fmt.Errorf robi to samo i zwraca error.
Jak wypisać strukturę z nazwami pól w Go?
Użyj %+v: fmt.Printf("%+v\n", user) wypisuje {Name:Ana Age:31}. %v wypisuje tylko wartości, {Ana 31}, a %#v wypisuje składnię Go razem z typem, main.User{Name:"Ana", Age:31}.
Jak sformatować liczbę zmiennoprzecinkową do 2 miejsc po przecinku w Go?
Użyj %.2f: fmt.Sprintf("%.2f", 3.14159) zwraca "3.14". Dodaj szerokość, żeby wyrównać kolumny, %8.2f, albo minus, żeby wyrównać do lewej, %-8.2f. strconv.FormatFloat(f, 'f', 2, 64) daje ten sam wynik bez stringa formatującego.
Co robi %w w fmt.Errorf?
%w formatuje błąd jak %v, a do tego go opakowuje, więc nowy błąd niesie oryginał. errors.Is i errors.As mogą wtedy znaleźć opakowany błąd: err := fmt.Errorf("load config: %w", os.ErrNotExist) sprawia, że errors.Is(err, os.ErrNotExist) zwraca true. %w działa tylko w fmt.Errorf.
Dlaczego w wyniku widzę %!d(string=...)?
Czasownik nie pasuje do typu argumentu, na przykład %d dostał string. fmt wypisuje problem w miejscu zamiast wywoływać panic: %!d(string=oops). Brakujące argumenty wypisują %!d(MISSING), a nadmiarowe %!(EXTRA int=2). go vet wyłapuje wszystkie trzy przypadki, zanim uruchomisz program.