Итоги: методы ввода данных
Краткое описание
Обобщаем три основных способа ввода данных от пользователя: 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)
}
Вывод: три метода покрывают большинство базовых сценариев ввода. Для строк с пробелами и сложной обработки нужны альтернативные способы (следующий урок).