$ sudo teach IT
МОДУЛЬ 2 · УРОК 2.2

Ввод/вывод. Работа с fmt

Учимся выводить текст на экран, форматировать данные и читать ввод от пользователя

В прошлом уроке мы научились создавать переменные и познакомились с основными типами данных: числа, строки, логические значения. Но программа, которая просто хранит данные внутри себя и ничего не показывает пользователю, бесполезна. Представьте калькулятор, который считает результат, но не выводит его на экран. Или навигатор, который знает маршрут, но молчит. Любая программа должна уметь две вещи: показывать информацию пользователю (вывод) и получать от него данные (ввод). Сегодня мы разберём пакет fmt — главный инструмент Go для ввода и вывода. А в конце урока напишем мини-игру «Угадай число».

📦 Что такое пакет fmt

Название fmt — это сокращение от слова format (форматирование). Этот пакет входит в стандартную библиотеку Go, а значит его не нужно устанавливать отдельно — он доступен сразу «из коробки». Вы уже видели fmt в программе «Hello, World!», когда писали fmt.Println("Hello, World!").

Пакет fmt содержит функции для двух задач:

  • Вывод — отображение текста и данных на экране (Println, Printf, Print)
  • Ввод — чтение данных, которые пользователь вводит с клавиатуры (Scan, Scanln, Scanf)

Чтобы использовать fmt, нужно импортировать его в начале файла. Это вы уже умеете:

package main

import "fmt"

func main() {
    fmt.Println("Привет!")
}

Давайте разберём каждую функцию подробно.

🖨 fmt.Println — простой вывод

Println расшифровывается как Print Line — «напечатать строку». Это самая простая и часто используемая функция вывода. Она печатает то, что вы ей передали, и автоматически переходит на новую строку.

package main

import "fmt"

func main() {
    fmt.Println("Привет, мир!")
    fmt.Println("Меня зовут Go.")
    fmt.Println("Мне нравится программировать.")
}

Результат в терминале:

Привет, мир!
Меня зовут Go.
Мне нравится программировать.

Обратите внимание: каждый Println выводит текст на отдельной строке. Это потому что функция автоматически добавляет символ перевода строки в конце.

Println может принимать сразу несколько значений через запятую. Она разделит их пробелами:

name := "Алексей"
age := 25
fmt.Println("Имя:", name, "Возраст:", age)

Результат:

Имя: Алексей Возраст: 25

Удобно: не нужно самостоятельно вставлять пробелы между значениями — Println делает это за вас. Можно передавать строки, числа, логические значения — вообще любые типы данных. Go сам поймёт, как их напечатать.

📄 fmt.Print — вывод без перехода на новую строку

Функция Print работает почти так же, как Println, но с одним отличием — она не добавляет перевод строки в конце. Это значит, что следующий вывод продолжится на той же строке:

fmt.Print("Привет, ")
fmt.Print("мир!")
fmt.Println() // пустой Println просто переводит строку
fmt.Print("Новая строка")

Результат:

Привет, мир!
Новая строка

Когда это полезно? Например, когда вы хотите вывести приглашение для ввода, чтобы пользователь печатал ответ на той же строке: fmt.Print("Введите ваше имя: "). Курсор останется сразу после двоеточия, и пользователь начнёт вводить текст рядом.

🎯 fmt.Printf — форматированный вывод

А вот это самая мощная функция вывода. Printf расшифровывается как Print Formatted — «напечатать с форматированием». Она позволяет точно контролировать, как именно данные будут выглядеть на экране.

Идея простая: вы пишете шаблон строки, а места, куда нужно подставить значения, отмечаете специальными символами — глаголами форматирования (format verbs). Они всегда начинаются со знака %.

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

Результат:

Меня зовут Мария, мне 30 лет.

Что здесь произошло? Go взял шаблон "Меня зовут %s, мне %d лет.\n" и заменил %s на значение переменной name (строку «Мария»), а %d — на значение переменной age (число 30). Символ \n в конце — это символ перевода строки. В отличие от Println, функция Printf сама не добавляет перевод строки, поэтому нужно делать это вручную через \n.

💡 Подсказка: обратите внимание на \n в конце строки формата. Если его не поставить, следующий вывод или приглашение терминала начнётся сразу после текста — без перехода на новую строку. Это частая ошибка у новичков. Запомните: Printf ничего не добавляет сам — что написали, то и получите.

🔤 Глаголы форматирования: %d, %s, %f, %v и другие

Каждый глагол говорит Go, какой тип данных подставлять и как его отображать. Давайте разберём самые важные.

%d — целое число

Используется для вывода целых чисел (int, int64 и т.д.). Буква «d» — от слова «decimal» (десятичное).

score := 95
fmt.Printf("Ваш результат: %d баллов\n", score)
// Ваш результат: 95 баллов

%s — строка

Используется для вывода строк (string). Буква «s» — от слова «string».

city := "Москва"
fmt.Printf("Город: %s\n", city)
// Город: Москва

%f — дробное число

Используется для вывода чисел с плавающей точкой (float64, float32). Буква «f» — от слова «float». По умолчанию выводит 6 знаков после запятой.

pi := 3.14159265
fmt.Printf("Число Пи: %f\n", pi)
// Число Пи: 3.141593

Шесть знаков после запятой — это многовато. Можно указать нужную точность, вставив число между % и f:

pi := 3.14159265
fmt.Printf("Пи (2 знака): %.2f\n", pi)   // 3.14
fmt.Printf("Пи (4 знака): %.4f\n", pi)   // 3.1416
fmt.Printf("Пи (0 знаков): %.0f\n", pi)  // 3

Запись %.2f означает: «выведи дробное число с двумя знаками после точки». Это очень полезно при работе с деньгами, процентами и любыми числами, где нужна определённая точность.

%v — универсальный глагол

Буква «v» — от слова «value» (значение). Этот глагол подходит для любого типа данных. Go сам определит, как лучше вывести значение. Когда лень думать, какой глагол использовать — берите %v.

name := "Анна"
age := 22
height := 1.68
student := true

fmt.Printf("Имя: %v\n", name)       // Имя: Анна
fmt.Printf("Возраст: %v\n", age)    // Возраст: 22
fmt.Printf("Рост: %v\n", height)    // Рост: 1.68
fmt.Printf("Студент: %v\n", student) // Студент: true

%T — тип переменной

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

x := 42
y := 3.14
z := "hello"

fmt.Printf("x: тип=%T, значение=%v\n", x, x) // x: тип=int, значение=42
fmt.Printf("y: тип=%T, значение=%v\n", y, y) // y: тип=float64, значение=3.14
fmt.Printf("z: тип=%T, значение=%v\n", z, z) // z: тип=string, значение=hello

💡 Подсказка: если вы используете не тот глагол (например, %d для строки), Go не выдаст ошибку при компиляции, но результат может быть странным. Поэтому следите за соответствием: %d для целых чисел, %s для строк, %f для дробных чисел. А если сомневаетесь — используйте %v, он работает с чем угодно.

📊 Шпаргалка по глаголам

Вот сводная таблица глаголов, которые вам понадобятся чаще всего. Не нужно заучивать всё наизусть — просто знайте, что такая таблица есть, и возвращайтесь к ней по мере необходимости.

%d — целое число (42, -7, 0)
%s — строка ("Привет")
%f — дробное число (3.14); %.2f — с точностью 2 знака
%v — любое значение (Go сам выберет формат)
%T — тип переменной (int, string, float64)
%t — логическое значение (true, false)
%% — вывести сам символ процента (%)
\n — перевод строки (не глагол, а спецсимвол)

🧪 Практика: комбинируем разные способы вывода

Давайте напишем программу, которая выводит информацию о товаре в интернет-магазине. Здесь мы используем все три функции вывода и несколько глаголов форматирования:

package main

import "fmt"

func main() {
    // Информация о товаре
    product := "Наушники Sony WH-1000XM5"
    price := 29990.00
    discount := 15 // процент скидки
    inStock := true

    // Вычисляем цену со скидкой
    finalPrice := price * (1 - float64(discount) / 100)

    // Выводим красивую карточку товара
    fmt.Println("===========================")
    fmt.Printf("Товар: %s\n", product)
    fmt.Printf("Цена: %.2f руб.\n", price)
    fmt.Printf("Скидка: %d%%\n", discount)
    fmt.Printf("Итого: %.2f руб.\n", finalPrice)
    fmt.Printf("В наличии: %t\n", inStock)
    fmt.Println("===========================")
}

Результат:

===========================
Товар: Наушники Sony WH-1000XM5
Цена: 29990.00 руб.
Скидка: 15%
Итого: 25491.50 руб.
В наличии: true
===========================

Обратите внимание на %d%% в строке со скидкой. Первый %d — это глагол для числа 15. А %% — это способ напечатать сам символ процента. Если написать просто %, Go подумает, что это начало нового глагола. Поэтому два процента подряд — %% — означают «просто выведи знак %».

⌨️ fmt.Scan — читаем ввод от пользователя

До сих пор все наши программы «говорили», но не «слушали». Пора научить программу получать данные от пользователя. Для этого используются функции fmt.Scan и fmt.Scanln.

Принцип простой: вы создаёте переменную, а затем передаёте в функцию Scan указатель на неё (через символ &). Программа остановится и будет ждать, пока пользователь что-нибудь введёт с клавиатуры и нажмёт Enter. Введённое значение запишется в вашу переменную.

package main

import "fmt"

func main() {
    var name string

    fmt.Print("Как вас зовут? ")
    fmt.Scan(&name)

    fmt.Printf("Приятно познакомиться, %s!\n", name)
}

Как это работает:

  1. var name string — создаём переменную name типа строка (пока пустую)
  2. fmt.Print("Как вас зовут? ") — выводим вопрос (без перевода строки, чтобы пользователь печатал на той же строке)
  3. fmt.Scan(&name) — программа останавливается и ждёт ввод. Пользователь пишет, например, «Андрей» и нажимает Enter. Значение «Андрей» записывается в переменную name
  4. fmt.Printf(...) — выводим приветствие с именем пользователя

Пример работы в терминале:

Как вас зовут? Андрей
Приятно познакомиться, Андрей!

💡 Подсказка: символ & перед переменной — это оператор взятия адреса. Пока не вдавайтесь в детали: просто запомните, что при использовании Scan перед переменной всегда нужно ставить &. Без него программа не скомпилируется. Мы подробнее разберём указатели позже в курсе.

🔢 Чтение чисел от пользователя

fmt.Scan автоматически определяет тип переменной и пытается преобразовать ввод пользователя в нужный тип. Если переменная — int, Go ожидает от пользователя целое число. Если float64 — дробное.

package main

import "fmt"

func main() {
    var age int

    fmt.Print("Сколько вам лет? ")
    fmt.Scan(&age)

    fmt.Printf("Через 10 лет вам будет %d!\n", age + 10)
}

Пример работы:

Сколько вам лет? 25
Через 10 лет вам будет 35!

А вот пример с дробными числами:

package main

import "fmt"

func main() {
    var weight float64

    fmt.Print("Ваш вес (кг): ")
    fmt.Scan(&weight)

    fmt.Printf("Ваш вес в фунтах: %.1f\n", weight * 2.205)
}

Пример работы:

Ваш вес (кг): 70.5
Ваш вес в фунтах: 155.5

Можно читать несколько значений за один вызов Scan:

var width, height float64

fmt.Print("Введите ширину и высоту: ")
fmt.Scan(&width, &height)

fmt.Printf("Площадь: %.2f\n", width * height)

В этом случае пользователь вводит два числа через пробел (или каждое на отдельной строке), а Scan запишет первое в width, второе в height.

📝 fmt.Scanln — чтение до конца строки

Функция fmt.Scanln (Scan Line) похожа на Scan, но с одним важным отличием: она прекращает чтение на символе перевода строки (когда пользователь нажимает Enter). Это значит, что все значения должны быть на одной строке.

var firstName, lastName string

fmt.Print("Введите имя и фамилию: ")
fmt.Scanln(&firstName, &lastName)

fmt.Printf("Добро пожаловать, %s %s!\n", firstName, lastName)

Пример работы:

Введите имя и фамилию: Иван Петров
Добро пожаловать, Иван Петров!

В чём разница между Scan и Scanln на практике? Scan читает значения, разделённые пробелами или переводами строк, и может «перетекать» на следующие строки. А Scanln читает только текущую строку. Для простых программ разница невелика, но Scanln обычно ведёт себя предсказуемее, когда вы читаете данные порциями — по одной строке за раз.

💡 Подсказка: для большинства задач в этом курсе используйте fmt.Scan — он проще. Переходите на fmt.Scanln, если сталкиваетесь со странным поведением при нескольких вызовах Scan подряд. Также существует fmt.Scanf (с шаблоном формата), но он используется значительно реже — мы его разберём, когда он понадобится.

🚧 Частые ошибки при вводе/выводе

Разберём типичные ошибки, с которыми сталкиваются новички, чтобы вы могли их избежать.

Забыли & перед переменной в Scan

Это самая популярная ошибка. Без & программа не скомпилируется.

var age int
fmt.Scan(age)   // ОШИБКА! Не скомпилируется
fmt.Scan(&age)  // Правильно

Несовпадение количества глаголов и значений в Printf

Количество глаголов (%d, %s и т.д.) в шаблоне должно совпадать с количеством переданных значений. Иначе будет предупреждение или некорректный вывод.

name := "Ира"
age := 20

// ПЛОХО: два глагола, но передано одно значение
fmt.Printf("Имя: %s, возраст: %d\n", name)

// ХОРОШО: два глагола, два значения
fmt.Printf("Имя: %s, возраст: %d\n", name, age)

Пользователь ввёл не то, что ожидалось

Если переменная типа int, а пользователь ввёл текст (например, «abc»), Scan вернёт ошибку и переменная останется с нулевым значением (0 для int). Пока мы не обрабатываем ошибки — это тема будущих уроков. Просто имейте это в виду и вводите правильные данные при тестировании.

Забыли \n в Printf

Printf не добавляет перевод строки автоматически. Если в конце шаблона нет \n, следующий вывод или приглашение терминала «прилипнет» к вашему тексту.

fmt.Printf("Привет")
fmt.Printf("Мир")
// Результат: ПриветМир (всё слиплось!)

fmt.Printf("Привет\n")
fmt.Printf("Мир\n")
// Результат:
// Привет
// Мир

🎮 Практика: программа «Угадай число»

Теперь давайте объединим всё, что мы сегодня узнали, в одну мини-игру. Программа загадает число, а пользователь попытается его угадать. Пока у нас нет циклов (мы их изучим в уроке 2.5), поэтому пользователь получит только одну попытку. Также у нас пока нет конструкции if/else (это следующий урок), поэтому мы просто покажем загаданное число и ответ пользователя, чтобы он сам сравнил.

Но подождите — как программе «загадать» число? Для этого нам понадобится пакет math/rand, который генерирует случайные числа. Давайте сразу посмотрим готовую программу, а потом разберём каждую строку:

package main

import (
    "fmt"
    "math/rand"
)

func main() {
    // Загадываем случайное число от 1 до 10
    secret := rand.IntN(10) + 1

    fmt.Println("=== Угадай число ===")
    fmt.Println("Я загадал число от 1 до 10.")
    fmt.Println("У тебя одна попытка!")
    fmt.Println()

    // Читаем ответ пользователя
    var guess int
    fmt.Print("Твоё число: ")
    fmt.Scan(&guess)

    // Выводим результат
    fmt.Println()
    fmt.Printf("Ты ввёл: %d\n", guess)
    fmt.Printf("Я загадал: %d\n", secret)

    fmt.Println()
    fmt.Println("Совпало? Проверь сам!")
    fmt.Println("(В уроке 2.4 мы научим программу")
    fmt.Println("проверять это автоматически)")
}

Пример работы программы:

=== Угадай число ===
Я загадал число от 1 до 10.
У тебя одна попытка!

Твоё число: 7

Ты ввёл: 7
Я загадал: 3

Совпало? Проверь сам!
(В уроке 2.4 мы научим программу
проверять это автоматически)

Давайте разберём новые моменты:

Импорт нескольких пакетов

Когда нужно импортировать больше одного пакета, используем скобки: import ("fmt" "math/rand"). Каждый пакет — на отдельной строке.

rand.IntN(10) + 1

rand.IntN(10) генерирует случайное число от 0 до 9 (включительно). Мы прибавляем 1, чтобы получить диапазон от 1 до 10. Почему от нуля? Так принято в программировании — отсчёт чаще всего начинается с нуля.

fmt.Println() без аргументов

Вызов fmt.Println() без текста просто выводит пустую строку. Это удобно для визуального разделения блоков текста в терминале.

💡 Подсказка: каждый раз при запуске программы rand.IntN будет генерировать разные числа. В старых версиях Go (до 1.20) нужно было вручную инициализировать генератор случайных чисел через rand.Seed, но начиная с Go 1.20 это происходит автоматически. Так что просто вызывайте rand.IntN — и всё будет работать.

🧑‍💻 Бонус: программа «Визитка»

Напоследок — ещё одна полноценная программа, которая собирает информацию от пользователя и красиво её выводит. Она использует все функции, которые мы сегодня изучили:

package main

import "fmt"

func main() {
    var name string
    var age int
    var city string
    var height float64

    fmt.Println("=== Генератор визиток ===")
    fmt.Println()

    fmt.Print("Ваше имя: ")
    fmt.Scan(&name)

    fmt.Print("Ваш возраст: ")
    fmt.Scan(&age)

    fmt.Print("Ваш город: ")
    fmt.Scan(&city)

    fmt.Print("Ваш рост (м): ")
    fmt.Scan(&height)

    // Выводим визитку
    fmt.Println()
    fmt.Println("+--------------------------+")
    fmt.Printf("|  Имя:    %-15s |\n", name)
    fmt.Printf("|  Возраст: %-14d |\n", age)
    fmt.Printf("|  Город:  %-15s |\n", city)
    fmt.Printf("|  Рост:   %-15.2f |\n", height)
    fmt.Println("+--------------------------+")
}

Пример работы:

=== Генератор визиток ===

Ваше имя: Алексей
Ваш возраст: 28
Ваш город: Казань
Ваш рост (м): 1.82

+--------------------------+
|  Имя:    Алексей          |
|  Возраст: 28              |
|  Город:  Казань           |
|  Рост:   1.82             |
+--------------------------+

Здесь есть новый трюк: %-15s. Число 15 — это ширина поля (минимальное количество символов). Минус перед числом означает выравнивание по левому краю. Благодаря этому столбцы визитки получаются ровными. Не обязательно запоминать это сейчас — просто знайте, что Printf позволяет управлять выравниванием и шириной вывода.

📌 Задание для самостоятельной работы

Чтобы закрепить материал, попробуйте написать эти программы самостоятельно:

Задание 1. Напишите программу, которая спрашивает у пользователя длину и ширину комнаты (в метрах, дробные числа) и выводит её площадь с двумя знаками после запятой.

Подсказка: площадь = длина * ширина. Для вывода используйте %.2f.

Задание 2. Напишите программу-конвертер температуры. Пользователь вводит температуру в Цельсиях, а программа выводит её в Фаренгейтах.

Подсказка: формула: F = C * 9/5 + 32. Используйте float64.

Задание 3. Напишите программу, которая спрашивает имя пользователя, год его рождения и выводит сообщение: «Привет, [имя]! В 2026 году тебе будет [возраст] лет.»

Подсказка: возраст = 2026 - год_рождения. Для красивого вывода используйте Printf.

📋 Итоги урока

  • fmt.Println — выводит текст и автоматически переходит на новую строку
  • fmt.Print — выводит текст без перевода строки (удобно для приглашений ввода)
  • fmt.Printf — форматированный вывод с глаголами: %d (целое число), %s (строка), %f (дробное), %v (любое значение), %T (тип)
  • %.2f — вывод дробного числа с заданной точностью (2 знака после точки)
  • fmt.Scan(&переменная) — читает ввод пользователя и записывает в переменную (не забывайте &!)
  • fmt.Scanln — аналог Scan, но читает только текущую строку
  • Пакет math/rand позволяет генерировать случайные числа
  • Мы написали мини-игру «Угадай число» — в будущих уроках мы добавим ей проверку ответа и несколько попыток

В следующем уроке мы разберём операторы и выражения: арифметические (+, -, *, /, %), сравнения (==, !=, <, >) и логические (&&, ||, !). Вы узнаете, как Go считает, сравнивает и принимает решения на основе данных. А в качестве практики напишем программу-калькулятор с выбором операции. До встречи в уроке 2.3!

Ввод/вывод. Работа с fmt — Язык Go – для начинающих! | $ sudo teach IT