Итоги: методы ввода данных

Краткое описание

Обобщаем три основных способа ввода данных от пользователя: fmt.Scan(), fmt.Scanln() и fmt.Scanf(). Разбираем их особенности, ограничения и типичные проблемы.

Три основных метода

1. fmt.Scan()

Особенность: читает данные до пробела или перевода строки.

var name string
fmt.Scan(&name)

Множественный ввод:

var a, b, c int
fmt.Scan(&a, &b, &c)
// Ввод: 10 20 30 (через пробел или Enter)

Смешанные типы:

var name string
var age int
var salary float64
fmt.Scan(&name, &age, &salary)
// Ввод: John 25 50000.5

2. fmt.Scanln()

Особенность: останавливается при встрече перевода строки (\n).

var x, y int
fmt.Scanln(&x, &y)
// Ввод: 10 20 [Enter] ✅ Оба числа в ОДНОЙ строке

Отличие от Scan:

// Если вводить с переносами:
// 10
// 20
// Scanln прочитает только 10, остановится на Enter

3. fmt.Scanf()

Особенность: форматированный ввод по шаблону.

var x, y int
fmt.Scanf("%d,%d", &x, &y)
// Ввод: 10,20 (через запятую)

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

// Дата через дефис
var day, month, year int
fmt.Scanf("%d-%d-%d", &day, &month, &year)
// Ввод: 30-10-2025

// Координаты со скобками
var x, y float64
fmt.Scanf("(%f, %f)", &x, &y)
// Ввод: (3.14, 2.71)

Сравнительная таблица

Метод Разделитель Останавливается на \n Формат Пример
Scan Пробел/Enter Нет Любой 10 20 или 10↵20
Scanln Пробел Да Любой 10 20 (одна строка)
Scanf По шаблону По шаблону Строгий 10-20-2025

Проверка количества прочитанных значений

Все методы возвращают два значения:

n, err := fmt.Scan(&a, &b, &c)
// n — количество успешно прочитанных значений
// err — ошибка (если есть)

Пример:

var a, b, c int
n, err := fmt.Scan(&a, &b, &c)

fmt.Printf("Прочитано: %d значений\n", n)
// Если ввели только "10 20", то n = 2

Основные ограничения

Ограничение #1: Пробелы в строках

Scan и Scanln не читают строки с пробелами полностью.

var fullName string
fmt.Scan(&fullName)
// Ввод: John Doe
// Результат: fullName = "John" (только до пробела!)
// "Doe" остаётся в буфере

Проблема: часть данных теряется или “застревает” в буфере.

Ограничение #2: Остатки в буфере

После ошибки или неполного ввода данные остаются в буфере.

var age int
fmt.Scan(&age)
// Ввод: abc (текст вместо числа)
// "abc" остаётся в буфере, мешает следующему вводу

Решение (упоминается, но детально не объясняется):

reader := bufio.NewReader(os.Stdin)
reader.ReadString('\n')  // Очищает буфер

Ограничение #3: Пустой ввод

Если пользователь просто нажимает Enter:

var input string
fmt.Scanln(&input)
// Пользователь: [Enter]
// Результат: input = "" (пустая строка)

Обработка: нужно проверять, что строка не пустая.

Ограничение #4: Scanf требует точного формата

Разделители в шаблоне должны совпадать с вводом.

fmt.Scanf("%d-%d-%d", &d, &m, &y)
// Ввод: 10.10.2025 ❌ (точки вместо дефисов)
// Ввод: 10-10-2025 ✅

Что делать с ошибками (кратко)

Базовая проверка:

n, err := fmt.Scan(&value)

if err != nil {
    fmt.Println("Ошибка ввода")
    // Очистка буфера (детали позже)
} else {
    fmt.Printf("Прочитано: %d значений\n", n)
}

Важно:

  • err != nil означает “есть ошибка”
  • Детали про if и error — в следующих уроках
  • Пока просто знайте: можно проверить, успешно ли прочитаны данные

Практические примеры

Пример 1: Базовый ввод

var name string
fmt.Print("Введите имя: ")
fmt.Scan(&name)
fmt.Printf("Привет, %s!\n", name)

Пример 2: Несколько чисел

var a, b, c int
fmt.Print("Введите 3 числа через пробел: ")
fmt.Scan(&a, &b, &c)
fmt.Printf("Сумма: %d\n", a+b+c)

Пример 3: Дата по шаблону

var day, month, year int
fmt.Print("Введите дату (ДД-ММ-ГГГГ): ")
fmt.Scanf("%d-%d-%d", &day, &month, &year)
fmt.Printf("Дата: %d.%d.%d\n", day, month, year)

Пример 4: Проверка количества

var x, y int
n, _ := fmt.Scan(&x, &y)
fmt.Printf("Прочитано значений: %d\n", n)
// Если ввели "10 abc", то n = 1

Пример 5: Смешанные типы

var name string
var age int
var height float64

fmt.Print("Введите имя, возраст и рост: ")
fmt.Scan(&name, &age, &height)
fmt.Printf("%s, %d лет, рост %.2f\n", name, age, height)

Ключевые моменты

1. Три метода — разное поведение

  • Scan — универсальный, игнорирует переносы
  • Scanln — только одна строка
  • Scanf — строгий формат

2. Всегда используйте & (амперсанд)

fmt.Scan(&variable)  // ✅

3. Пробел = разделитель

fmt.Scan(&a, &b)
// 10 20 ✅

4. Строки с пробелами — проблема

fmt.Scan(&fullName)
// "John Doe" → считается только "John"

5. Можно проверить успешность

n, err := fmt.Scan(&value)
// n — сколько прочитано
// err — есть ли ошибка

6. Буфер нужно очищать

После ошибок данные остаются в буфере (детали позже).

7. Scanf требует точного совпадения

fmt.Scanf("%d-%d", &a, &b)
// "10-20" ✅
// "10,20" ❌

Когда что использовать

Задача Метод Пример
Одно значение Scan fmt.Scan(&age)
Несколько через пробел Scanln fmt.Scanln(&x, &y)
Строгий формат Scanf fmt.Scanf("%d-%d", &a, &b)
Строка с пробелами ❌ Не подходят Нужен другой способ

Что запомнить

  • Scan — читает до пробела/переноса, продолжает на новой строке
  • Scanln — читает только одну строку (до Enter)
  • Scanf — читает по шаблону (формат важен)
  • Все требуют & перед переменной
  • Пробел = разделитель значений
  • Строки с пробелами не читаются полностью
  • Можно проверить количество прочитанных значений (n)
  • Можно проверить наличие ошибки (err != nil)
  • После ошибок нужно очищать буфер (детали позже)
  • Для сложных сценариев — другие методы (следующий урок)

Итоговый пример

package main

import "fmt"

func main() {
    // 1. Простой ввод
    var name string
    fmt.Print("Имя: ")
    fmt.Scan(&name)
    
    // 2. Несколько значений
    var age int
    var height float64
    fmt.Print("Возраст и рост: ")
    fmt.Scan(&age, &height)
    
    // 3. Форматированный ввод
    var day, month, year int
    fmt.Print("Дата (ДД-ММ-ГГГГ): ")
    fmt.Scanf("%d-%d-%d", &day, &month, &year)
    
    // 4. Проверка количества
    var x, y int
    n, _ := fmt.Scanln(&x, &y)
    fmt.Printf("Прочитано: %d\n", n)
    
    fmt.Printf("%s, %d лет, %.2f м\n", name, age, height)
    fmt.Printf("Дата: %02d.%02d.%d\n", day, month, year)
}

Вывод: три метода покрывают большинство базовых сценариев ввода. Для строк с пробелами и сложной обработки нужны альтернативные способы (следующий урок).