Пакет 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 имеет строгую типизацию без автоматического приведения типов.
Решения:
- Использовать форматирование:
fmt.Printf("Мне %d лет", age) - Использовать
fmt.Sprintf()для создания строки - Явно конвертировать число в строку (об этом позже в курсе)
Print vs Println: ключевые различия
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-е)