Как прочитать ввод с клавиатуры (стандартный ввод) в консоли на Go?
Проблема с fmt.Scan и решение через bufio.Scanner
Ваш пример с fmt.Scan действительно не работает для строк с пробелами, потому что fmt.Scan (как и fmt.Scanf) разбивает ввод по пробельным символам. Для чтения целых строк и чисел с обработкой ошибок лучше использовать пакет bufio в связке с os.Stdin. Это самый гибкий и надёжный способ.
Пошаговое решение: читаем строку, затем число
Вот полный пример программы, которая запрашивает имя (с пробелами) и возраст, а затем выводит приветствие:
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
func main() {
// Создаём сканер для чтения с клавиатуры
scanner := bufio.NewScanner(os.Stdin)
// Запрашиваем имя
fmt.Print("Введите ваше имя: ")
scanner.Scan() // читаем следующую строку
name := scanner.Text() // получаем строку как есть
// Проверяем ошибку сканера (например, если stdin закрыт)
if err := scanner.Err(); err != nil {
fmt.Println("Ошибка чтения имени:", err)
return
}
// Запрашиваем возраст
fmt.Print("Введите ваш возраст: ")
scanner.Scan()
ageStr := scanner.Text()
// Убираем лишние пробелы
ageStr = strings.TrimSpace(ageStr)
// Преобразуем строку в число
age, err := strconv.Atoi(ageStr)
if err != nil {
fmt.Println("Ошибка: возраст должен быть числом, а вы ввели:", ageStr)
return
}
// Вывод результата
fmt.Printf("Привет, %s! Тебе %d лет.\n", name, age)
}
Как это работает
- bufio.NewScanner(os.Stdin) — создаёт сканер, который читает из стандартного ввода. Удобно тем, что читает целыми строками (до символа новой строки).
- scanner.Scan() — считывает следующую строку. Возвращает
true, если есть данные, иfalseпри ошибке или конце файла. - scanner.Text() — возвращает считанную строку без символа новой строки.
- strconv.Atoi — преобразует строку в число. Если строка не является числом, возвращает ошибку. Альтернатива:
strconv.ParseIntдля других разрядностей. - strings.TrimSpace — полезно, чтобы удалить случайные пробелы в начале/конце ввода.
Обработка нескольких чисел в одной строке
Если нужно прочитать несколько чисел, разделённых пробелами, используйте fmt.Scan или разбивайте строку через strings.Fields:
// Чтение двух чисел из одной строки
fmt.Print("Введите два числа через пробел: ")
scanner.Scan()
line := scanner.Text()
parts := strings.Fields(line)
if len(parts) != 2 {
fmt.Println("Ошибка: нужно ввести ровно два числа")
return
}
a, _ := strconv.Atoi(parts[0])
b, _ := strconv.Atoi(parts[1])
fmt.Println("Сумма:", a+b)
Важные моменты
- После
scanner.Scan()всегда проверяйтеscanner.Err()— это поможет отловить неожиданные ошибки (например, если ввод был перенаправлен из файла и он повреждён). - Если вы читаете много строк (например, в цикле), создавайте один
scannerи используйте его повторно — это эффективно. - Для простых случаев (одно слово или число) можно использовать
fmt.Scan, но для реальных программ лучшеbufio.Scanner.
Этот подход считается идиоматичным в Go и используется в большинстве консольных утилит.
<<Альтернатива: fmt.Scanf и fmt.Scanln
Если вам нужно прочитать строго форматированный ввод (например, "Имя Возраст" в одной строке), можно использовать fmt.Scanf или fmt.Scanln. Они работают аналогично scanf из C.
package main
import "fmt"
func main() {
var name string
var age int
fmt.Print("Введите имя и возраст через пробел: ")
// Читаем строку, слово и число
_, err := fmt.Scanf("%s %d", &name, &age)
if err != nil {
fmt.Println("Ошибка ввода:", err)
return
}
fmt.Printf("Привет, %s! Тебе %d лет.\n", name, age)
}
Недостатки этого подхода
%sчитает только до первого пробела, поэтому "Иван Иванов" не сработает — прочитается только "Иван".- Если пользователь введёт лишние символы, они останутся в буфере и могут "сломать" следующий ввод.
- Требуется точное соответствие шаблону — при несовпадении возвращается ошибка.
Когда использовать
fmt.Scanf удобен для быстрых скриптов или когда формат ввода строго контролируется (например, в тестах). Для интерактивных программ с пользователем лучше выбрать bufio.Scanner из первого ответа — он прощает ошибки и обрабатывает строки любой длины.
Ещё один вариант — fmt.Scanln, который читает до символа новой строки, но также разбивает по пробелам. Он подходит для чтения нескольких значений с одной строки без форматирования.