Перейти к основному содержимому
Модуль 8 · Урок 1 из 536/50 в курсе~10 min
Уроки модуля (1/5)

Расширенный `fmt`

Пакет fmt — это шлюз для всего текстового ввода-вывода в Go. Хорошо это знать — значит писать читаемые журналы, выявлять информативные ошибки и идиоматически форматировать значения, не прибегая к внешним библиотекам.

Четыре семьи

fmt предоставляет функции четырех семейств, различающихся префиксом:

  • Print* → записывает в os.Stdout (Print, Println, Printf).
  • Fprint* → записывает в любой io.Writer (Fprintln(os.Stderr, ...), Fprintf(file, ...)).
  • Sprint* → возвращает строку без печати (Sprintf, Sprintln).
  • Errorf → создает отформатированный error.
Go
fmt.Println("hello")                       // stdout
fmt.Fprintln(os.Stderr, "boom")            // stderr
s := fmt.Sprintf("[%d]", 42)               // "[42]"
err := fmt.Errorf("parse %q: %w", in, base) // wrapped error

Разница между Print и Println заключается только в пробеле между аргументами и завершающей новой строкой: Println всегда добавляет \n, Print нет, Printf соответствует формату.

Основные глаголы

ГлаголЧто он делает
%vЗначение по умолчанию (вызывает String(), если реализовано).
%+vСтруктура с именами полей.
%#vСинтаксис Go (полезен для отладки и создания снимков).
%TДинамический тип значения.
%dЦелое число по основанию 10.
%b %o %x %XДвоичный, восьмеричный, шестнадцатеричный (строчные/прописные).
%f %e %gПлавающая точка: фиксированная, экспоненциальная, компактная.
%sСтрока (или error/Stringer).
%qСтрока в стиле Go в кавычках (включая escape-символы).
%cСимвол Юникода (rune).
%pУказатель в шестнадцатеричном формате.
%wПеренос ошибок (только внутри Errorf).
Go
type Point struct{ X, Y int }
p := Point{1, 2}
fmt.Printf("%v\n", p)   // {1 2}
fmt.Printf("%+v\n", p)  // {X:1 Y:2}
fmt.Printf("%#v\n", p)  // main.Point{X:1, Y:2}
fmt.Printf("%T\n", p)   // main.Point

Ширина, точность, флаги

Числовые и строковые команды принимают ширину и точность:

  • %6d → минимальная ширина 6, левое дополнение пробелами.
  • %-6d → ширина 6, отступ справа (флаг -).
  • %06d → ширина 6, дополнение 0 (флаг 0).
  • %.2f → точность до 2 десятичных цифр.
  • %8.2f → ширина 8, точность 2.
Go
fmt.Printf("|%6d|\n", 42)    // |    42|
fmt.Printf("|%-6d|\n", 42)   // |42    |
fmt.Printf("|%06d|\n", 42)   // |000042|
fmt.Printf("|%8.2f|\n", 3.14)// |    3.14|

Для выровненных таблиц фиксированная ширина + %-...s для текстовых столбцов является самым простым шаблоном перед переходом на text/tabwriter.

%w и перенос ошибок

%w особенный: он работает только внутри fmt.Errorf и сохраняет исходную ошибку в цепочке, так что errors.Is и errors.As могут пройти по ней.

Go
if _, err := os.Open(path); err != nil {
    return fmt.Errorf("read config %q: %w", path, err)
}

Использование %v вместо %w печатает сообщение об ошибке, но разрывает цепочку: нет развертывания, нет errors.Is. Правило: если вы хотите, чтобы вызывающий абонент мог различить тип или идентификатор ошибки, используйте %w (только один на каждый Errorf).

Упражнения

Упражнение#go.m8.l1.e1
Попыток: 0Загрузка…

Используйте fmt.Sprintf, чтобы отформатировать строку «[42]» из целого числа 42, а затем распечатать ее.

Загрузка редактора…

Решение доступно после 3 попыток

Упражнение#go.m8.l1.e2
Попыток: 0Загрузка…

Выведите структуру p с глаголом %+v (она должна включать имена полей).

Загрузка редактора…

Решение доступно после 3 попыток

Quiz#go.m8.l1.e3
Готово

Какой глагол, используемый в fmt.Errorf, сохраняет цепочку ошибок для ошибок.Is/As?

Go
err := fmt.Errorf("read %q: ???", path, base)
Варианты ответа