1. Навіщо нам fmt.Printf, якщо є fmt.Println
Коли ви лише починаєте програмувати, fmt.Println здається ідеальним: він друкує значення, сам розставляє пробіли та автоматично додає переведення рядка. Але саме тоді, коли ви намагаєтеся зрозуміти, чому щось працює не так, Println стає надто «добрим» і трохи приховує деталі. fmt.Printf — це режим, у якому ви самі керуєте виводом: де будуть пробіли, де переведення рядка, як саме друкувати значення і який у них тип.
Уявіть, що Println — це кафе, де вам приносять «комбо № 1», а Printf — це конструктор: ви самі обираєте булку, котлету, соус і навіть те, як саме буде оформлено ваше замовлення. Іноді «комбо» достатньо. Але для налагодження й акуратного виводу частіше потрібен саме конструктор.
Невелике порівняння:
| Функція | Що робить | Переведення рядка | Коли зручна |
|---|---|---|---|
|
Друкує без форматування | Ні | Короткі фрагменти тексту без прикрас |
|
Друкує та додає пробіли між аргументами | Так | Швидкий вивід, щоб просто побачити результат |
|
Друкує за шаблоном, тобто за форматним рядком | Ні, зазвичай потрібен \n | Акуратний вивід і діагностика типів та значень |
2. Форматний рядок і специфікатори
Щоб fmt.Printf був справді корисним, потрібно зрозуміти просту річ: у Printf є форматний рядок — це звичайний текст, усередині якого є спеціальні «віконця» для підстановки значень. Ці «віконця» називаються специфікаторами (або format verbs) і записуються як %....
Важливий технічний факт, який зараз корисно тримати в голові: сигнатура fmt.Printf виглядає так — першим аргументом іде рядок формату, а далі — будь-яка кількість значень.
Тобто схема проста: спочатку шаблон, потім дані.
Зручно уявляти роботу Printf ось так:
flowchart LR
A["Форматний рядок: 'x=%d type=%T '"] --> C["fmt.Printf"]
B["Аргументи: x, x"] --> C["fmt.Printf"]
C --> D["стандартний вивід: x=10 type=int"]
І тепер найважливіше правило: кількість специфікаторів у форматному рядку має збігатися з кількістю переданих значень. Інакше ви отримаєте або %!(EXTRA …) у виводі, або %!(MISSING) — і це виглядатиме як прокляття древніх богів.
3. Основні специфікатори та переведення рядка
%v: універсальний «покажи значення якось нормально»
%v — це специфікатор, який рятує, коли ви не хочете думати, який саме тип друкуєте. Він означає value, тобто значення, і друкує його у стандартному представленні.
Коли ви налагоджуєте програму, найчастіше вам потрібно просто побачити, що зараз лежить у змінній. Не красиво, не в таблицю, не у звіт, а просто: «що там?». Ось тут %v — ваш найкращий помічник.
Мініприклад:
package main
import "fmt"
func main() {
x := 10
fmt.Printf("x=%v\n", x) // x=10
}
Зауважте: Printf сам переведення рядка не робить. Тому \n наприкінці — це не для краси, а спосіб не дати наступному виводу «прилипнути» до цього.
Ще один приклад із кількома типами:
package main
import "fmt"
func main() {
name := "Гофер"
ok := true
fmt.Printf("name=%v ok=%v\n", name, ok) // name=Гофер ok=true
}
Тут %v зручний тим, що однаково спокійно працює і з рядками, і з булевими значеннями, і з числами. Для діагностики — ідеально.
%T: друкуємо тип і перестаємо гадати
%T друкує тип значення, яке ви передали в Printf. Це дуже корисно, коли ви використовуєте := і покладаєтеся на виведення типу (type inference), а потім дивуєтеся, чому вираз не компілюється або чому порівняння «не те».
У документації та матеріалах із Go часто показують комбінацію %v + %T саме як спосіб одночасно побачити значення та його тип.
Це буквально рентген для змінних.
Приклад «значення + тип»:
package main
import "fmt"
func main() {
x := 10
fmt.Printf("x=%v type=%T\n", x, x) // x=10 type=int
}
Сенс дуже практичний: ви починаєте мислити як компілятор Go. Не «ну це ж число», а «це int». І поступово зникає магічне мислення.
Мінідетектив: чому тип узагалі може виявитися неочікуваним
Іноді тип дивує через літерали та типи за замовчуванням. Наприклад, рядковий літерал зазвичай має тип string, цілочисельний — int, дробовий — зазвичай float64. У матеріалах із Go часто пояснюють: коли неявно типізовані (untyped) значення передають у Printf, вони отримують «тип за замовчуванням». Саме тому %T так корисний.
У тему констант сьогодні не заглиблюватимемося, але як практичний прийом це дуже зручно: якщо сумніваєтеся — друкуйте %T.
%d і %s: строгий режим для акуратного виводу
%v — універсальний. Але якщо ви друкуєте дані для користувача або хоча б для себе через тиждень, іноді корисно ввімкнути «строгий режим»: друкувати числа як числа, рядки як рядки. Так вивід стає акуратнішим і допомагає швидше знаходити помилки.
- %d — для цілих чисел (зазвичай int).
- %s — для рядків (string).
Приклад: друк простого виразу «як у підручнику»:
package main
import "fmt"
func main() {
a := 2
b := 3
fmt.Printf("%d + %d = %d\n", a, b, a+b) // 2 + 3 = 5
}
Приклад: рядковий шаблон для вітання:
package main
import "fmt"
func main() {
name := "Анна"
fmt.Printf("Привіт, %s!\n", name) // Привіт, Анна!
}
Такий вивід одразу виглядає як програма, а не як набір налагоджувальних роздруківок.
\n: переведення рядка — це ваша відповідальність
У fmt.Println переведення рядка додається автоматично. У fmt.Printf — ні. Це не баг і не «чому Go такий строгий», а просто принцип: якщо ви керуєте форматом, то керуєте всім, у тому числі переведенням рядка.
Тому, якщо ви хочете виводити рядки окремо, найчастіше наприкінці форматного рядка пишуть \n.
Поганий приклад (вивід злипнеться):
package main
import "fmt"
func main() {
fmt.Printf("first")
fmt.Printf("second")
fmt.Printf("third")
// firstsecondthird
}
Нормальний приклад:
package main
import "fmt"
func main() {
fmt.Printf("first\n") // first
fmt.Printf("second\n") // second
fmt.Printf("third\n") // third
}
Відтепер вважайте, що переведення рядка вже під вашим контролем.
4. Приклад: TripBudget
Давайте розвивати простий навчальний застосунок TripBudget — калькулятор бюджету поїздки. Ми поки не використовуємо масиви, зрізи, функції та не будуємо складну архітектуру. Ми просто читаємо введення, рахуємо й акуратно друкуємо результат. На цьому рівні нам важливіше навчитися бачити типи й значення, ніж будувати систему.
Версія 0: швидко працює
Уявімо, що в нас є такі змінні: місто, кількість днів і бюджет на день (у цілих доларах). Ми хочемо порахувати загальну суму.
package main
import "fmt"
func main() {
city := "Берлін"
days := 4
perDay := 70
total := days * perDay
fmt.Println(city, days, perDay, total) // Берлін 4 70 280
}
Це працює, але вивід надто сирий. Користувачеві незрозуміло, що з цього є що.
Версія 1: вивід для людей
package main
import "fmt"
func main() {
city := "Берлін"
days := 4
perDay := 70
total := days * perDay
fmt.Printf("Поїздка до %s: %d днів, $%d на день, усього = $%d\n", city, days, perDay, total)
// Поїздка до Берліна: 4 днів, $70 на день, усього = $280
}
Тепер вивід читається як нормальне повідомлення, а не як «дамп пам’яті».
Версія 2: діагностичний режим
Іноді ви змінюєте код, переносите змінні, додаєте нові — і раптом починаєте сумніватися: «а days точно int?», «а city точно string?». Ось тут ми додаємо %T.
package main
import "fmt"
func main() {
city := "Берлін"
days := 4
perDay := 70
fmt.Printf("city=%v type=%T\n", city, city) // city=Берлін type=string
fmt.Printf("days=%v type=%T\n", days, days) // days=4 type=int
fmt.Printf("perDay=%v type=%T\n", perDay, perDay) // perDay=70 type=int
}
Це особливо корисно, коли ви активно використовуєте :=, тому що тип виводиться автоматично, і часом ви самі собі влаштовуєте сюрприз.
5. Патерни Printf, які економлять час
Зараз буде кілька патернів, які варто запам’ятати. Я навмисно роблю їх короткими, щоб вони легше осідали в пам’яті.
Показати все в одному рядку
Коли ви налагоджуєте умову або обчислення, зручно вивести все одразу:
package main
import "fmt"
func main() {
a := 10
b := 3
fmt.Printf("a=%d b=%d a/b=%d a%%b=%d\n", a, b, a/b, a%b)
// a=10 b=3 a/b=3 a%b=1
}
Зверніть увагу на a%%b: щоб надрукувати символ % у форматному рядку, ви пишете %%. Це не нова тема «форматування», а просто маленька побутова деталь: % у рядку формату — службовий символ.
Перевірити, що вираз справді bool
Дуже корисно друкувати не лише числа, а й логічні вирази:
package main
import "fmt"
func main() {
age := 17
canEnter := age >= 18
fmt.Printf("age=%d canEnter=%v type(canEnter)=%T\n", age, canEnter, canEnter)
// age=17 canEnter=false type(canEnter)=bool
}
Це допомагає перестати плутати значення й умову. У Go умова обов’язково має бути bool, і %T добре закріплює це на практиці.
Передали не те — побачили одразу
Іноді ви випадково друкуєте змінну не тим специфікатором. Наприклад, рядок — %d. Тоді у виводі ви одразу побачите щось дивне. Це хороший сигнал: «я друкую не те, чим це здається».
Хороша звичка: якщо вивід виглядає дивно, додайте поруч %T і подивіться, що ви насправді друкуєте.
6. Типові помилки під час роботи з fmt.Printf
Помилка № 1: забули \n і отримали «злиплий» вивід.
Це класика: ви друкуєте кілька рядків підряд, очікуєте акуратні повідомлення, а вони перетворюються на одну довгу «ковбасу». Причина проста: Printf не додає переведення рядка автоматично. Якщо ви хочете виводити рядки окремо, \n — ваш обов’язок.
Помилка № 2: не збіглася кількість специфікаторів і кількість аргументів.
Коли у форматному рядку три «віконця» (%d %d %d), а ви передали два значення або чотири, вивід починає нагадувати повідомлення інопланетян. У цей момент не потрібно панікувати: просто перечитайте форматний рядок і порахуйте, скільки підстановок ви насправді зробили.
Помилка № 3: переплутали %d і %s.
Ця помилка особливо неприємна новачкам, бо «ну воно ж виглядає як текст або число». Але Go — мова, яка поважає типи. %d — для цілих чисел, %s — для рядків. Якщо ви сумніваєтеся, який у вас тип, — додайте %T і перестаньте гадати.
Помилка № 4: використовують %v всюди й втрачають читабельність.
%v чудово підходить для діагностики, але якщо ви робите «людський» вивід, %d і %s часто читаються краще: вони дисциплінують формат і вас самого. Хороший баланс такий: у налагодженні — %v/%T, у користувацькому повідомленні — точніші %d/%s.
Помилка № 5: намагаються зробити Printf «як Println» і дивуються пробілам.
Println сам ставить пробіли між аргументами. Printf цього не робить: пробіли, коми, двокрапки та інша пунктуація — частина форматного рядка. Тобто якщо ви не написали пробіл, його не буде. Спочатку це дратує, а потім перетворюється на суперсилу: ви починаєте друкувати рівно так, як задумали.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ