Содержание

Руководство по чтению ввода с клавиатуры в Go

Многие первые программы общаются с пользователем через терминал: спрашивают имя, читают команду, принимают число, обрабатывают строки до EOF. Для этого Go использует обычную модель потоков: ввод приходит из stdin, вывод идет в stdout, ошибки - в stderr.

Клавиатура в терминале для программы выглядит как поток байтов. Пользователь печатает текст, нажимает Enter, и программа получает строку. Пакеты os, bufio, fmt и strings помогают читать этот поток разными способами.

os.Stdin

os.Stdin - стандартный поток ввода:

fmt.Fprintln(os.Stdout, "Введите имя:")

Для чтения его обычно оборачивают в bufio.Scanner или bufio.Reader.

Важно: stdin не всегда означает клавиатуру. Данные можно передать через pipe:

echo "Alice" | go run main.go

Для программы это все равно os.Stdin.

bufio.Scanner

Для чтения строк в учебных CLI чаще всего подходит bufio.Scanner:

scanner := bufio.NewScanner(os.Stdin) fmt.Print("Введите имя: ") if !scanner.Scan() { if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "ошибка ввода:", err) } return } name := strings.TrimSpace(scanner.Text()) fmt.Printf("Привет, %s!\n", name)

Scan читает следующую строку. Text возвращает текст без \n. TrimSpace убирает пробелы в начале и конце.

Scanner удобен, когда ввод состоит из строк:

  • имя пользователя;
  • команды;
  • строки файла;
  • список значений, по одному на строку.

Чтение до EOF

EOF означает end of file - конец ввода. В терминале его можно отправить через Ctrl+D на macOS/Linux или Ctrl+Z, затем Enter на Windows.

scanner := bufio.NewScanner(os.Stdin) for scanner.Scan() { line := scanner.Text() fmt.Println("line:", line) } if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "read input:", err) }

Такой код подходит для программ, которые работают с pipe:

cat input.txt | go run main.go

Лимит Scanner

У Scanner есть ограничение размера token. Для обычных строк это нормально, но для очень длинных строк может быть мало.

Можно увеличить буфер:

scanner := bufio.NewScanner(os.Stdin) buffer := make([]byte, 0, 64*1024) scanner.Buffer(buffer, 1024*1024)

Если вы читаете большие строки или произвольные блоки данных, иногда лучше использовать bufio.Reader.

bufio.Reader

bufio.Reader дает больше контроля:

reader := bufio.NewReader(os.Stdin) fmt.Print("Введите текст: ") text, err := reader.ReadString('\n') if err != nil && !errors.Is(err, io.EOF) { fmt.Fprintln(os.Stderr, "ошибка ввода:", err) return } text = strings.TrimSpace(text) fmt.Println("Вы ввели:", text)

ReadString('\n') читает до перевода строки и включает разделитель в результат. Поэтому часто нужен strings.TrimSpace.

ReadBytes и ReadString

ReadString возвращает строку:

line, err := reader.ReadString('\n')

ReadBytes возвращает []byte:

data, err := reader.ReadBytes('\n')

Если дальше вы работаете с текстом, удобнее ReadString. Если с байтами, файлами или протоколами, может быть удобнее ReadBytes.

fmt.Fscan

Для чтения простых значений можно использовать fmt.Fscan:

var age int fmt.Print("Введите возраст: ") if _, err := fmt.Fscan(os.Stdin, &age); err != nil { fmt.Fprintln(os.Stderr, "ожидалось число") return } fmt.Println("Возраст:", age)

Fscan удобен для задач, где ввод строго форматирован: например, в алгоритмических упражнениях. Для пользовательских CLI он часто менее дружелюбен, потому что хуже работает с произвольными строками и понятными ошибками.

Scanner или Reader

ЗадачаПодход
Читать строки по однойbufio.Scanner
Читать до EOFbufio.Scanner
Читать очень длинные строкиbufio.Reader
Читать до конкретного разделителяReadString или ReadBytes
Читать числа из строго форматированного вводаfmt.Fscan

Начинайте со Scanner, если нет причины брать более низкоуровневый инструмент.

Полный пример

package main import ( "bufio" "fmt" "os" "strings" ) func main() { scanner := bufio.NewScanner(os.Stdin) fmt.Print("Введите имя: ") if !scanner.Scan() { if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "ошибка ввода:", err) } os.Exit(1) } name := strings.TrimSpace(scanner.Text()) if name == "" { fmt.Fprintln(os.Stderr, "имя не должно быть пустым") os.Exit(1) } fmt.Printf("Привет, %s!\n", name) }

Здесь есть три важные привычки:

  • ввод очищается через TrimSpace;
  • ошибка чтения проверяется;
  • ошибка пользователя пишется в stderr.

Частые ошибки

  1. Игнорировать ошибку после Scan.
  2. Забывать TrimSpace и получать строку с пробелами.
  3. Использовать Fscan для ввода имени с пробелами.
  4. Считать, что stdin всегда клавиатура.
  5. Не различать stdout и stderr.
  6. Использовать Scanner для очень больших строк без увеличения буфера.

Чеклист

  1. Для обычных строк используйте bufio.Scanner.
  2. Для больших или специальных случаев используйте bufio.Reader.
  3. После чтения проверяйте ошибку.
  4. Пользовательский текст очищайте через strings.TrimSpace.
  5. Ошибки пишите в os.Stderr.
  6. Помните, что ввод может прийти из pipe, файла или терминала.

Следующий шаг после статьи

Закрепите тему во вводном проекте без регистрации, а затем переходите к курсам.

Продолжить изучение

Выбери следующую статью по маршруту или углубись в смежную тему.

Похожие статьи