Форматирование строк в Go без печати: полное руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: Зачем форматировать строку, если не нужно печатать?

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

Этот вопрос мучает тысячи разработчиков, и не зря — на Stack Overflow он собрал десятки тысяч просмотров. В этой статье мы разберем как форматировать строки в Go без печати, используя реальные примеры кода. Вы узнаете о мощном инструменте fmt.Sprintf, о современных подходах с strings.Builder и о низкоуровневом bytes.Buffer. Поехали!

Проблема: Printf печатает, а мне нужно сохранить

В Go есть замечательная функция fmt.Printf, которая форматирует строку и сразу выводит её в стандартный поток вывода. Но что если вы хотите получить отформатированную строку как значение, а не как побочный эффект? Например:

name := "Алиса"
age := 30
// Хочу получить строку "Меня зовут Алиса, мне 30 лет"
// но не печатать её сразу

Начинающие разработчики часто пишут так:

fmt.Printf("Меня зовут %s, мне %d лет", name, age)
// Строка напечатана, но мы её потеряли!

Это работает, но результат улетает в консоль. А нам нужно сохранить его в переменную для дальнейшей обработки. Решение есть, и оно элегантное.

Решение №1: fmt.Sprintf — король форматирования

В пакете fmt есть функция-близнец Printf, которая называется Sprintf. Буква "S" в начале означает "String" — она возвращает строку, а не печатает её. Это самый прямой и понятный способ форматирования строк в Go без печати.

Базовый пример с Sprintf

package main

import (
    "fmt"
)

func main() {
    name := "Боб"
    age := 25
    score := 98.5

    // Форматируем без печати
    message := fmt.Sprintf("Привет, %s! Тебе %d лет. Твой результат: %.1f баллов.", name, age, score)

    // Теперь message содержит отформатированную строку
    fmt.Println(message) // Печатаем только сейчас
}

Вывод:

Привет, Боб! Тебе 25 лет. Твой результат: 98.5 баллов.

Обратите внимание: Sprintf использует те же самые плейсхолдеры ( %s, %d, %f ), что и Printf. Разница только в том, что результат возвращается, а не выводится.

Продвинутые плейсхолдеры

Вы можете использовать все возможности форматирования Go:

package main

import "fmt"

func main() {
    // Выравнивание по ширине
    leftAligned := fmt.Sprintf("%-10s %d", "Товар", 10)
    rightAligned := fmt.Sprintf("%10s %d", "Товар", 10)

    // Форматирование чисел
    price := 1234.5678
    formattedPrice := fmt.Sprintf("Цена: $%.2f (с НДС: $%.2f)", price, price*1.2)

    // Шестнадцатеричный вывод
    hexValue := fmt.Sprintf("Hex: %x, Dec: %d", 255, 255)

    fmt.Println(leftAligned)
    fmt.Println(rightAligned)
    fmt.Println(formattedPrice)
    fmt.Println(hexValue)
}

Вывод:

Товар      10
      Товар 10
Цена: $1234.57 (с НДС: $1481.48)
Hex: ff, Dec: 255

fmt.Sprintf — это ваш универсальный инструмент для форматирования строк Go. Он работает быстро, понятен и покрывает 90% задач.

Решение №2: strings.Builder — для сложных конструкций

Что если вам нужно собрать строку из множества частей, добавляя их по одной? Например, формировать HTML, JSON или SQL-запрос. Использовать Sprintf для каждой части неэффективно — вы будете создавать много промежуточных строк, нагружая сборщик мусора. На помощь приходит strings.Builder.

Как работает strings.Builder

strings.Builder — это структура, которая накапливает строки внутри себя, минимизируя выделение памяти. После сборки вы получаете готовую строку одним вызовом String().

package main

import (
    "fmt"
    "strings"
)

func main() {
    var sb strings.Builder

    // Добавляем части строки
    sb.WriteString("Пользователь: ")
    sb.WriteString("Иван")
    sb.WriteString(", возраст: ")
    sb.WriteString(fmt.Sprintf("%d", 30))
    sb.WriteString(", статус: ")
    sb.WriteString("активен")

    // Получаем итоговую строку
    result := sb.String()
    fmt.Println(result)
}

Вывод:

Пользователь: Иван, возраст: 30, статус: активен

Почему это лучше, чем конкатенация?

Наивная конкатенация через + создаёт новую строку при каждом сложении. Если у вас 100 частей, будет создано 100 промежуточных строк. strings.Builder выделяет память один раз и расширяет буфер по мере необходимости. Для больших объёмов данных разница в производительности может быть колоссальной.

Продвинутый пример: генерация CSV

package main

import (
    "fmt"
    "strings"
)

func main() {
    var sb strings.Builder

    // Заголовок CSV
    sb.WriteString("Имя,Возраст,Город\n")

    // Данные
    data := []struct {
        name string
        age  int
        city string
    }{
        {"Анна", 28, "Москва"},
        {"Петр", 35, "СПб"},
        {"Елена", 22, "Казань"},
    }

    for _, row := range data {
        sb.WriteString(fmt.Sprintf("%s,%d,%s\n", row.name, row.age, row.city))
    }

    csvData := sb.String()
    fmt.Println(csvData)
}

Вывод:

Имя,Возраст,Город
Анна,28,Москва
Петр,35,СПб
Елена,22,Казань

strings.Builder идеален, когда вы форматируете строки в Go в цикле или собираете их из множества фрагментов.

Решение №3: bytes.Buffer — низкоуровневая мощь

До появления strings.Builder (Go 1.10) разработчики использовали bytes.Buffer. Этот инструмент всё ещё актуален, особенно если вы работаете с байтами, а не только со строками. bytes.Buffer реализует интерфей

Блоги

Рекомендаций по книгам