Пакет fmt: форматированный вывод в Go

Краткое описание

В этом уроке изучаем один из самых используемых пакетов стандартной библиотеки Go — fmt. Разбираем конкатенацию строк, разницу между Print, Println и Printf, работу со спецификаторами формата и создание строк через Sprintf. Узнаём об ограничении Go — невозможности конкатенировать строку и число напрямую из-за строгой типизации.

Ключевые концепции

Конкатенация строк: оператор +

Строки в Go можно склеивать через оператор +:

firstName := "Сергей"
lastName := "К."

fullName := firstName + " " + lastName  // "Сергей К."
greeting := "Привет, " + firstName + "!"  // "Привет, Сергей!"

Важно: пробелы не добавляются автоматически — их нужно указывать явно.

Критическое ограничение: нельзя смешивать строки и числа

В Go нельзя конкатенировать строку и число напрямую (в отличие от C#, JavaScript, Python):

age := 25

// message := "Мне " + age + " лет"  // ❌ ОШИБКА компиляции!
// invalid operation: "Мне " + age (mismatched types string and int)

Причина: Go имеет строгую типизацию без автоматического приведения типов.

Решения:

  1. Использовать форматирование: fmt.Printf("Мне %d лет", age)
  2. Использовать fmt.Sprintf() для создания строки
  3. Явно конвертировать число в строку (об этом позже в курсе)

fmt.Print

fmt.Print("Hello")
fmt.Print("World")
fmt.Print("!\n")

// Вывод: HelloWorld!
// - НЕ добавляет пробелы между аргументами
// - НЕ переходит на новую строку

fmt.Println

fmt.Println("Hello", "World", "!")

// Вывод: Hello World !
// - Автоматически добавляет пробелы между аргументами
// - Автоматически переходит на новую строку

Практика:

name := "Алиса"
age := 30
isActive := true

// Имя: Алиса Возраст: 30 Активен: true
fmt.Println("Имя:", name, "Возраст:", age, "Активен:", isActive)

Printf: форматированный вывод с шаблонами

Printf использует спецификаторы формата (пришли из языка C и Unix-систем):

userName := "Мария"
userAge := 28

fmt.Printf("Меня зовут %s и мне %d лет\n", userName, userAge)
// Меня зовут Мария и мне 28 лет

Важно: Printf не добавляет перенос строки автоматически — нужно явно добавлять \n.

Основные спецификаторы

Спецификатор Тип Пример
%d Целое число (decimal) 42
%f Вещественное число (float) 3.14
%s Строка (string) "Hello"
%t Булево значение (true/false) true
%v Универсальный (любой тип) любое значение
%T Тип переменной int, string

Примеры:

product := "Ноутбук"
price := 75000
discount := 15

// Товар: Ноутбук, Цена: 75000 руб., Скидка: 15%
fmt.Printf("Товар: %s, Цена: %d руб., Скидка: %d%%\n", product, price, discount)

Внимание: чтобы вывести символ %, нужно экранировать его: %%

%v — универсальный спецификатор (ваша страховка)

Главный секрет: если забыли какой спецификатор использовать — пишите %v. Он работает с любым типом:

intValue := 42
stringValue := "Go"
floatValue := 3.14
boolValue := true

// Все работает!
fmt.Printf("Число: %v\n", intValue)      // 42
fmt.Printf("Строка: %v\n", stringValue)  // Go
fmt.Printf("Float: %v\n", floatValue)    // 3.14
fmt.Printf("Bool: %v\n", boolValue)      // true

%v — ваш главный инструмент для отладки.

%T — узнать тип переменной

mystery := 100

fmt.Printf("Значение: %v, Тип: %T\n", mystery, mystery)
// Значение: 100, Тип: int

Полезно для отладки, когда не уверены в типе данных.

Точность вещественных чисел: %.2f

По умолчанию %f выводит 6 знаков после запятой:

pi := 3.14159265359

fmt.Printf("%f\n", pi)    // 3.141593
fmt.Printf("%.2f\n", pi)  // 3.14
fmt.Printf("%.4f\n", pi)  // 3.1416
fmt.Printf("%.0f\n", pi)  // 3

Формат: %.Nf — где N — количество знаков после запятой.

Системы счисления: %b, %o, %x, %X

Go позволяет выводить числа в разных системах счисления:

number := 255

fmt.Printf("Двоичная:          %b\n", number)  // 11111111
fmt.Printf("Восьмеричная:      %o\n", number)  // 377
fmt.Printf("Десятичная:        %d\n", number)  // 255
fmt.Printf("Шестнадцатеричная: %x\n", number)  // ff (нижний регистр)
fmt.Printf("Шестнадцатеричная: %X\n", number)  // FF (верхний регистр)

Применение: вывод цветов в HEX, работа с низкоуровневыми данными, отладка.

Практический пример:

red, green, blue := 255, 255, 254

fmt.Printf("RGB: (%d, %d, %d)\n", red, green, blue)
// RGB: (255, 255, 254)

fmt.Printf("HEX: #%02X%02X%02X\n", red, green, blue)
// HEX: #FFFFFE

fmt.Sprintf: форматирование без вывода

Ключевое отличие: Sprintf возвращает строку, а не выводит её:

name := "Сергей"
age := 35

// Sprintf СОЗДАЁТ строку, но НЕ выводит её
message := fmt.Sprintf("Привет, меня зовут %s и мне %d лет", name, age)

// Теперь можем использовать эту строку
fmt.Println(message)
// Привет, меня зовут Сергей и мне 35 лет

Применение:

1. Динамические имена файлов

fileNumber := 42
fileName := fmt.Sprintf("document_%03d.txt", fileNumber)
// document_042.txt

%03d означает: зарезервировать 3 символа, дополнить нулями слева.

2. Логирование

errorCode := 404
errorMessage := "Not Found"
logEntry := fmt.Sprintf("[ERROR %d]: %s", errorCode, errorMessage)
// [ERROR 404]: Not Found

3. Форматированные сообщения

product := "Мышь"
price := 1299.50
priceTag := fmt.Sprintf("%s - %.2f руб.", product, price)
// Мышь - 1299.50 руб.

4. Вложенные Sprintf

result := fmt.Sprintf("Внешний: %s", 
    fmt.Sprintf("Вложенный: %d", 123))
// Внешний: Вложенный: 123

Практические примеры

Полный пример использования

package main

import "fmt"

func main() {
    // 1. Конкатенация
    firstName := "Сергей"
    greeting := "Привет, " + firstName + "!"
    fmt.Println(greeting)

    // 2. Println с автоматическими пробелами
    name := "Алиса"
    age := 30
    fmt.Println("Имя:", name, "Возраст:", age)

    // 3. Printf с форматированием
    fmt.Printf("Меня зовут %s и мне %d лет\n", name, age)

    // 4. Универсальный %v
    fmt.Printf("Значение: %v, Тип: %T\n", age, age)

    // 5. Вещественные числа с точностью
    price := 1299.99
    fmt.Printf("Цена: %.2f руб.\n", price)

    // 6. Системы счисления
    number := 255
    fmt.Printf("Dec: %d, Hex: %X, Bin: %b\n", number, number, number)

    // 7. Sprintf для создания строк
    fileName := fmt.Sprintf("file_%03d.txt", 42)
    fmt.Println(fileName)  // file_042.txt
}

Форматирование цен и чисел

// Резервирование места с нулями
errorCode := 404
fmt.Printf("Error: %05d\n", errorCode)  // Error: 00404

// Резервирование места с пробелами
fmt.Printf("Error: %5d\n", errorCode)   // Error:   404

// Цена с двумя знаками
price := 3499.5
fmt.Printf("Цена: %.2f руб.\n", price)  // Цена: 3499.50 руб.

Важные моменты

1. Строгая типизация — нельзя смешивать типы

Go не делает автоматическое приведение типов при конкатенации. Это предотвращает ошибки.

2. Printf не добавляет \n

В отличие от Println, нужно явно добавлять перенос строки.

3. %v — универсальная страховка

Забыли спецификатор? Используйте %v — работает всегда. Идеально для отладки.

4. Экранирование символа %

Чтобы вывести %, пишите %%:

fmt.Printf("Скидка: 15%%\n")  // Скидка: 15%

5. Sprintf не выводит на экран

Sprintf возвращает строку, которую нужно либо сохранить, либо передать дальше.

6. %T для отладки

Используйте %T, чтобы узнать тип переменной во время разработки.

7. Точность float по умолчанию — 6 знаков

%f выводит 6 знаков после запятой. Для контроля используйте %.Nf.

8. Традиция из Unix/C

Спецификаторы формата пришли из языка C (1970-е годы) и используются во многих языках: Go, C, C++, Python, PHP, Ruby.

Best Practices

1. Для отладки используйте %v

// Хорошо - универсально
fmt.Printf("Debug: %v\n", someValue)

// Можно лучше - явный тип
fmt.Printf("Debug: %d\n", intValue)

2. Используйте Sprintf для переиспользуемых строк

// Хорошо - создаём строку для дальнейшего использования
logMessage := fmt.Sprintf("[%s] User %d logged in", timestamp, userID)
logger.Info(logMessage)
writeToFile(logMessage)

// Плохо - дублирование
fmt.Printf("[%s] User %d logged in\n", timestamp, userID)
// ... где-то ещё тот же код

3. Не злоупотребляйте конкатенацией

// Плохо - много конкатенаций
result := "Hello " + name + ", you are " + /* ошибка! */ age + " years old"

// Хорошо - используйте форматирование
result := fmt.Sprintf("Hello %s, you are %d years old", name, age)

4. Форматируйте цены с фиксированной точностью

// Хорошо - всегда 2 знака
fmt.Printf("%.2f руб.\n", price)  // 1299.50 руб.

// Плохо - непредсказуемое количество знаков
fmt.Printf("%f руб.\n", price)    // 1299.500000 руб.

5. Используйте %03d для нумерации файлов

// Хорошо - файлы отсортируются правильно
for i := 1; i <= 100; i++ {
    fileName := fmt.Sprintf("file_%03d.txt", i)
    // file_001.txt, file_002.txt, ..., file_100.txt
}

// Плохо - неправильная сортировка
// file_1.txt, file_10.txt, file_2.txt...

Что запомнить

  • Конкатенация строк через оператор +
  • Нельзя конкатенировать строку и число напрямую (строгая типизация)
  • fmt.Print — без пробелов, без переноса
  • fmt.Println — с пробелами, с переносом
  • fmt.Printf — форматированный вывод, без переноса (нужен \n)
  • Основные спецификаторы: %d (int), %f (float), %s (string), %t (bool)
  • %v — универсальный спецификатор (работает с любым типом)
  • %T — показывает тип переменной
  • %.2f — вещественное число с 2 знаками после запятой
  • Системы счисления: %b (bin), %o (oct), %d (dec), %x/%X (hex)
  • %% — вывод символа процента
  • fmt.Sprintf — создаёт строку без вывода на экран
  • %03d — зарезервировать 3 символа, дополнить нулями
  • Спецификаторы формата пришли из языка C и Unix-систем (1970-е)

Полезные ссылки