fmt — полный разборconsole.log("Hello") → fmt.Println("Hello")
console.log("x =", x) → fmt.Println("x =", x) (автоматический пробел между аргументами)
console.log({ a: 1, b: 2 }) → fmt.Printf("%+v\n", obj) (вывод структуры с полями)
util.format("x=%d", 10) → fmt.Sprintf("x=%d", 10)
JSON.stringify(obj, null, 2) → spew.Dump(obj) или кастомный sprintf("%+v")
fmt: Print, Fprint, Sprint — семейства%v, %+v, %#v, %T, %d, %s, %f и др.Stringer и GoStringererrors и fmt.Errorf с %wfmt.Scan, fmt.SscanfFormatterfmtПакет fmt — аналог console + util.format в Node.js. Он реализует форматированный I/O с помощью функций, разделённых на три семейства:
| Семейство | Вывод | Сигнатура |
|---|---|---|
| stdout | Print(a...any), Printf(format, a...), Println(a...) | |
| Fprint | любой io.Writer | Fprint(w, a...), Fprintf(w, format, a...), Fprintln(w, a...) |
| Sprint | возвращает строку | Sprint(a...), Sprintf(format, a...), Sprintln(a...) |
Правило запоминания: Первая буква — куда выводим (P = stdout, F = File/Writer, S = String). Суффикс — формат (f = format, ln = newline, без суффикса = значения через пробел).
name := "Алиса"
age := 30
fmt.Print("Hello, ") // Hello,
fmt.Print(name, "!\n") // Hello, Алиса!
fmt.Println("Age:", age) // Age: 30 (с переносом строки)
fmt.Printf("%s is %d years old\n", name, age) // Алиса is 30 years old
Используется для записи в файлы, HTTP-ответы, буферы — любой объект, реализующий io.Writer.
var buf bytes.Buffer
fmt.Fprintf(&buf, "User: %s, Score: %d", name, score)
// buf.String() → "User: Алиса, Score: 42"
// Запись в файл
f, _ := os.Create("output.txt")
defer f.Close()
fmt.Fprintln(f, "лог-строка") // эквивалент f.WriteString("лог-строка\n")
Аналог в Node.js: fs.writeFileSync('file.txt', util.format(...))
s := fmt.Sprintf("Hello, %s! You have %d messages.", name, count)
// s = "Hello, Алиса! You have 5 messages."
Используется для построения сообщений, SQL-запросов (осторожно с инъекциями!), шаблонов.
| Вербо | Назначение | Пример | Результат |
|---|---|---|---|
%v | Значение (value) по умолчанию | fmt.Printf("%v", user) | {Алиса 30} |
%+v | Значение с именами полей | fmt.Printf("%+v", user) | {Name:Алиса Age:30} |
%#v | Go-синтаксис представления | fmt.Printf("%#v", user) | main.User{Name:"Алиса", Age:30} |
%T | Тип значения | fmt.Printf("%T", user) | main.User |
%t | bool | fmt.Printf("%t", true) | true |
%d | Десятичное целое | fmt.Printf("%d", 42) | 42 |
%b | Двоичное | fmt.Printf("%b", 42) | 101010 |
%x / %X | Шестнадцатеричное | fmt.Printf("%x", 255) | ff / FF |
%f | Число с плавающей точкой | fmt.Printf("%f", 3.14) | 3.140000 |
%.2f | С точностью | fmt.Printf("%.2f", 3.1415) | 3.14 |
%s | Строка | fmt.Printf("%s", "hello") | hello |
%q | Строка в кавычках | fmt.Printf("%q", "hello") | "hello" |
%p | Указатель | fmt.Printf("%p", &x) | 0xc0000b2000 |
%% | Литерал процента | fmt.Printf("100%%") | 100% |
type User struct {
Name string
Age int
}
u := User{"Боб", 25}
fmt.Printf("1: %v\n", u) // 1: {Боб 25}
fmt.Printf("2: %+v\n", u) // 2: {Name:Боб Age:25}
fmt.Printf("3: %#v\n", u) // 3: main.User{Name:"Боб", Age:25}
fmt.Printf("4: %T\n", u) // 4: main.User
fmt.Printf("|%10s|%10s|\n", "Name", "Age") // | Name| Age|
fmt.Printf("|%-10s|%-10s|\n", "Name", "Age") // |Name |Age |
fmt.Printf("|%10d|%10d|\n", 42, 7) // | 42| 7|
Аналог в Node.js: padStart/padEnd или util.format с %10s.
n := 1234567.89
fmt.Printf("%%.2f: %.2f\n", n) // 1234567.89
fmt.Printf("%,.2f: %,.2f\n", n) // 1,234,567.89 (Windows)
fmt.Printf("%%10.2f: %10.2f\n", n) // " 1234567.89"
fmt.Printf("%%e: %e\n", n) // 1.234568e+06
Stringer и кастомное форматированиеfmt.StringerЭто аналог toString() в JavaScript/Java. Определяет, как объект выводится через %v, %s, Println.
type Stringer interface {
String() string
}
type User struct {
Name string
Age int
}
// String реализует fmt.Stringer
func (u User) String() string {
return fmt.Sprintf("👤 %s (%d лет)", u.Name, u.Age)
}
func main() {
u := User{"Алиса", 30}
fmt.Println(u) // 👤 Алиса (30 лет)
fmt.Printf("%v\n", u) // 👤 Алиса (30 лет)
fmt.Printf("%s\n", u) // 👤 Алиса (30 лет)
}
GoStringerИспользуется с %#v. Позволяет контролировать, как тип выглядит в Go-синтаксисе.
func (u User) GoString() string {
return fmt.Sprintf("User{Name: %q, Age: %d}", u.Name, u.Age)
}
fmt.Printf("%#v", u) // User{Name: "Алиса", Age: 30}
Formatter (продвинутый)Позволяет полностью контролировать форматирование для всех вербо.
type User struct{ Name string }
func (u User) Format(f fmt.State, verb rune) {
switch verb {
case 'v':
if f.Flag('+') {
fmt.Fprint(f, "User("+u.Name+")")
return
}
fmt.Fprint(f, u.Name)
case 's':
fmt.Fprint(f, u.Name)
case 'q':
fmt.Fprintf(f, "%q", u.Name)
}
}
fmt.Errorf и %werr := fmt.Errorf("ошибка подключения к БД: %s", dbName)
%w (Go 1.13+)Позволяет создавать цепочки ошибок, которые можно разворачивать через errors.Is и errors.As.
import "errors"
var ErrNotFound = errors.New("запись не найдена")
func GetUser(id int) (User, error) {
// ... запрос в БД
return User{}, fmt.Errorf("GetUser(%d): %w", id, ErrNotFound)
}
func main() {
_, err := GetUser(42)
fmt.Println(err) // GetUser(42): запись не найдена
fmt.Println(errors.Is(err, ErrNotFound)) // true
}
// ⚠️ НЕЛЬЗЯ использовать два %w — скомпилируется, но работать будет неправильно
err := fmt.Errorf("A: %w, B: %w", err1, err2) // работает только err1
Для множественной обёртки используйте errors.Join (Go 1.20+).
fmt.Scan, fmt.Sscanfvar name string
var age int
fmt.Print("Введите имя и возраст: ")
n, err := fmt.Scan(&name, &age)
if err != nil {
fmt.Println("Ошибка ввода:", err)
return
}
fmt.Printf("Прочитано %d значений: %s, %d\n", n, name, age)
Sscanf)Аналог sscanf из C. Разбирает строку по шаблону.
input := "Алиса 30 65.5"
var name string
var age int
var weight float64
fmt.Sscanf(input, "%s %d %f", &name, &age, &weight)
fmt.Printf("Name: %s, Age: %d, Weight: %.1f\n", name, age, weight)
// Name: Алиса, Age: 30, Weight: 65.5
⚠️ Важно: fmt.Scan и fmt.Sscanf останавливаются на пробелах. Для построчного чтения используйте bufio.Scanner.
fmt.Fscanln — чтение строки с пробеламиvar line string
fmt.Scanln(&line) // читает до новой строки, включая пробелы
fmt.Fscan — чтение из любого readerreader := strings.NewReader("hello 42")
var s string
var n int
fmt.Fscan(reader, &s, &n) // s="hello", n=42
var _ fmt.Stringer = (*User)(nil) // compile-time check
func Log(level, msg string, fields ...any) {
fmt.Fprintf(os.Stderr, "[%s] %s | fields=%+v\n", level, msg, fields)
}
Log("ERROR", "БД недоступна", "db", "pgx", "timeout", "5s")
// [ERROR] БД недоступна | fields=[db:pgx timeout:5s]
func Debug(v any) {
fmt.Fprintf(os.Stderr, "--- DEBUG ---\n%+v\n--- END ---\n", v)
}
query := fmt.Sprintf("SELECT * FROM users WHERE id = %d", userID)
// ⚠️ Если userID пришёл от пользователя — SQL injection!
// Для БД используйте placeholders: $1, $2
func PrintTable(header []string, rows [][]string) {
// Формирование строки с шириной
line := strings.Repeat("+", 40)
fmt.Println(line)
for _, h := range header {
fmt.Printf("| %-15s", h)
}
fmt.Println("|")
for _, row := range rows {
for _, cell := range row {
fmt.Printf("| %-15s", cell)
}
fmt.Println("|")
}
}
| Задача | Node.js | Go |
|---|---|---|
| Вывод в консоль | console.log(x) | fmt.Println(x) |
| Форматированная строка | util.format("x=%d", n) | fmt.Sprintf("x=%d", n) |
| Вывод объекта | console.log(obj) | fmt.Printf("%+v\n", obj) |
| Deep pretty-print | console.dir(obj, {depth: null}) | spew.Dump(obj) |
| Запись в файл | fs.writeFileSync(f, str) | fmt.Fprintln(f, str) |
| toString() | obj.toString() | func (o Obj) String() string |
| Ошибка с контекстом | new Error("msg: " + ctx) | fmt.Errorf("msg: %w", err) |
| Парсинг строки | sscanf (нет встроенного) | fmt.Sscanf(s, "%d", &n) |
fmt три семейства: Print (stdout), Fprint (Writer), Sprint (string)%v (значение), %+v (с полями), %#v (Go-syntax), %T (тип)String() для своих типов — это улучшает читаемость логовfmt.Errorf("… %w …", err) — стандартный способ оборачивания ошибокgithub.com/davecgh/go-spew/spew