В пакете fmt три семейства функций печати, и в каждом одни и те же три варианта:
| Функция | Куда идёт вывод | Формат |
|---|---|---|
Print, Println, Printf | стандартный вывод | по умолчанию, по умолчанию с пробелами и переводом строки, строка формата |
Sprint, Sprintln, Sprintf | возвращаемая string | те же три стиля |
Fprint, Fprintln, Fprintf | любой io.Writer (файл, буфер, HTTP-ответ) | те же три стиля |
Errorf | возвращаемая error | строка формата плюс %w для оборачивания |
Println ставит пробелы между операндами и перевод строки в конце. Printf ничего не добавляет: \n вы пишете сами. Print ставит пробелы только между операндами, которые оба не строки; это настолько неожиданно, что в большинстве кода используют Println или Printf.
Глаголы формата
Общие
| Глагол | Что печатает | Пример вывода |
|---|---|---|
%v | значение в формате по умолчанию | {Ana 31 [admin]} |
%+v | структуры с именами полей | {Name:Ana Age:31 Tags:[admin]} |
%#v | значение в синтаксисе Go | main.User{Name:"Ana", Age:31, Tags:[]string{"admin"}} |
%T | тип | main.User |
%% | знак процента | % |
%+v лучше всего подходит для отладки. Указатель на структуру печатается как &{...}, а не как адрес. Мапы печатаются с отсортированными ключами, поэтому вывод стабилен, хотя порядок обхода мапы случайный.
Целые числа
| Глагол | Значение | fmt.Sprintf(verb, 255) |
|---|---|---|
%d | десятичное | 255 |
%b | двоичное | 11111111 |
%o | восьмеричное | 377 |
%O | восьмеричное с префиксом 0o | 0o377 |
%x / %X | шестнадцатеричное, строчные или заглавные | ff / FF |
%#x | шестнадцатеричное с префиксом 0x | 0xff |
%c | символ с этой кодовой точкой | ÿ |
%q | символьный литерал в кавычках | 'ÿ' |
%U | формат Unicode | U+00FF |
Числа с плавающей точкой
| Глагол | Значение | fmt.Sprintf(verb, 1234.5678) |
|---|---|---|
%f | десятичная запись, по умолчанию 6 знаков | 1234.567800 |
%.2f | десятичная запись, 2 знака | 1234.57 |
%e | экспоненциальная запись | 1.234568e+03 |
%g | %e или %f, что короче, без хвостовых нулей | 1234.5678 |
%v | то же, что %g | 1234.5678 |
%.2f округляет точное двоичное значение float, поэтому fmt.Sprintf("%.2f", 2.675) даёт 2.67: ближайший к 2.675 float64 чуть меньше этого числа. Никогда не форматируйте деньги из float; храните копейки в целом числе.
Строки и байты
| Глагол | Значение | fmt.Sprintf(verb, "go\n") |
|---|---|---|
%s | строка как есть | go и перевод строки |
%q | в двойных кавычках, escape-последовательности видны | "go\n" |
%x | hex каждого байта | 676f0a |
% x | hex через пробел | 67 6f 0a |
%s для []byte печатает его как текст; %v печатает числа ([104 105]).
Другие типы
| Глагол | Тип | Что печатает |
|---|---|---|
%t | bool | true или false |
%p | указатель, слайс, мапа, канал, функция | адрес, например 0xc000012345 |
%w | error (только в Errorf) | сообщение ошибки, и оборачивает её |
Ширина, точность и выравнивание
Между % и глаголом можно поставить флаги, ширину и точность:
| Форма | Эффект |
|---|---|
%5d | ширина 5, выравнивание вправо (слева дополняется пробелами) |
%-5d | ширина 5, выравнивание влево |
%05d | дополнение нулями |
%.2f | 2 цифры после десятичной точки |
%8.2f | ширина 8 и 2 знака после точки |
%.3s | не больше 3 символов строки |
%+d | всегда показывать знак |
%*d | ширина берётся из следующего аргумента |
Заметьте: %.0f для 2.5 печатает 2, здесь Go округляет половину к чётному. Для строк ширина считается в рунах, а не в экранных колонках, поэтому иероглифы и эмодзи всё равно могут сбить выравнивание таблицы. Для ровных колонок с текстом переменной длины измерения за вас сделает text/tabwriter.
Индексы аргументов
%[n] выбирает аргумент по позиции, что позволяет использовать его повторно:
fmt.Printf("%[2]s %[1]s\n", "world", "hello") // hello world
fmt.Printf("%d %[1]x %[1]b\n", 10) // 10 a 1010
Errorf и %w
fmt.Errorf создаёт error из строки формата. С %w она ещё и оборачивает другую ошибку, чтобы вызывающий код мог распознать исходную:
Используйте %w, когда вызывающему коду может понадобиться проверить причину, и %v, когда вы намеренно её скрываете. Начиная с Go 1.20 один вызов Errorf может содержать несколько глаголов %w. Оборачивание подробно разобрано на странице об обработке ошибок.
Свой формат через String()
Любой тип с методом String() string сам определяет, как его показывают %v, %s и Println:
%d обходит String() и печатает базовое число. Для типов ошибок аналогичный метод Error() string, и он имеет приоритет над String().
Если глагол неверный
fmt никогда не паникует из-за неправильного формата. Он печатает проблему прямо в выводе:
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)
Такой вывод часто доезжает до продакшена, потому что ничего не падает. go vet ловит все три случая при сборке:
./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
Заметки о производительности
fmt принимает каждый аргумент как any и проверяет его тип во время выполнения (для структур, слайсов и мап используя рефлексию). Для логов и вывода это нормально, но в плотных циклах заметно. Чтобы преобразовать одно число, strconv.Itoa и strconv.FormatFloat быстрее, чем Sprintf. Чтобы собрать длинную строку в цикле, пишите в strings.Builder через fmt.Fprintf(&b, ...), а не склеивайте результаты Sprintf.
Часто задаваемые вопросы
Чем отличаются Println, Printf и Sprintf в Go?
fmt.Println печатает аргументы через пробел и добавляет перевод строки в конце. fmt.Printf печатает по строке формата и перевод строки не добавляет. fmt.Sprintf форматирует так же, как Printf, но возвращает результат строкой, а не печатает. fmt.Errorf делает то же самое и возвращает error.
Как вывести структуру с именами полей в Go?
Используйте %+v: fmt.Printf("%+v\n", user) печатает {Name:Ana Age:31}. %v печатает только значения, {Ana 31}, а %#v выводит синтаксис Go вместе с типом, main.User{Name:"Ana", Age:31}.
Как вывести float с двумя знаками после запятой в Go?
Используйте %.2f: fmt.Sprintf("%.2f", 3.14159) возвращает "3.14". Добавьте ширину для выравнивания колонок, %8.2f, или минус для выравнивания влево, %-8.2f. strconv.FormatFloat(f, 'f', 2, 64) даёт тот же результат без строки формата.
Что делает %w в fmt.Errorf?
%w форматирует ошибку как %v и заодно оборачивает её, так что новая ошибка несёт в себе исходную. Тогда errors.Is и errors.As могут найти обёрнутую ошибку: после err := fmt.Errorf("load config: %w", os.ErrNotExist) выражение errors.Is(err, os.ErrNotExist) истинно. %w работает только в fmt.Errorf.
Почему в выводе появляется %!d(string=...)?
Глагол не соответствует типу аргумента, например %d получил строку. fmt не паникует, а печатает проблему прямо в выводе: %!d(string=oops). Недостающие аргументы печатаются как %!d(MISSING), лишние как %!(EXTRA int=2). go vet ловит все три случая ещё до запуска программы.