← Все этапы
04 · ПАКЕТЫ И НАДЁЖНОСТЬУрок 24 из 3335 мин

Потоки чтения и записи

Читаем данные частями, корректно обрабатываем конец потока и используем стандартные буферы.

Запускаем программу

Создайте файл main.go и сохраните в нём программу. Она копирует данные между реализациями интерфейсов, не привязываясь к конкретному файлу или сетевому соединению.

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"
)

func main() {
	source := strings.NewReader("Казань\nТомск\nПермь\n")
	var copied bytes.Buffer

	byteCount, err := io.Copy(&copied, source)
	if err != nil {
		fmt.Println("ошибка копирования:", err)
		return
	}

	scanner := bufio.NewScanner(strings.NewReader(copied.String()))
	lineNumber := 0
	for scanner.Scan() {
		lineNumber++
		fmt.Printf("%d. %s\n", lineNumber, scanner.Text())
	}
	if err := scanner.Err(); err != nil {
		fmt.Println("ошибка чтения строк:", err)
		return
	}

	fmt.Println("прочитано байт:", byteCount)
}

Ожидаемый вывод содержит три пронумерованных города и число прочитанных байтов — 35. Важно, что это число измеряет байты в UTF-8, поэтому оно не равно числу букв для русских слов.

Reader и Writer описывают поведение

Интерфейс io.Reader требует метод Read(p []byte) (n int, err error): реализация читает до размера переданного буфера и сообщает, сколько байтов действительно записала в него. Интерфейс io.Writer требует Write(p []byte) (n int, err error) и сообщает, сколько байтов принял. Файл, строка, буфер и сетевое соединение могут реализовать эти же контракты, поэтому функция может работать с поведением, а не с конкретным типом.

Буфер передают читающему объекту; читатель не обязан заполнять его целиком за один вызов. Используйте только диапазон p[:n]: оставшиеся элементы буфера не содержат новых данных. При прямом цикле чтения обрабатывайте сначала n > 0, а затем ошибку. Последний вызов может одновременно вернуть байты и io.EOF; эти байты ещё нужно сохранить. io.EOF означает нормальный конец потока, а другая ошибка сообщает о сбое.

Если вручную пишете в io.Writer, проверяйте и err, и число n: по контракту короткая запись должна сопровождаться ошибкой. Если реализация всё же вернула n < len(p) без ошибки, считайте это io.ErrShortWrite. Высокоуровневые функции из пакета io обычно снимают с вызывающего кода необходимость самостоятельно склеивать короткие чтения и записи.

Буферизированное чтение

Пакет bufio оборачивает Reader буфером и добавляет удобные способы чтения. bufio.Reader подходит, когда нужно читать строку или разделённые данные; метод ReadString('\n') может вернуть непустую строку вместе с io.EOF, если последняя строка не заканчивается переводом строки. Сначала используйте возвращённые данные, затем обработайте ошибку.

Для последовательного чтения токенов часто удобен bufio.Scanner. Метод Scan перемещает сканер к следующему токену, а Text возвращает его текст. После цикла обязательно проверяйте Err: завершение сканирования бывает вызвано как концом входа, так и ошибкой. У Scanner есть максимальный размер токена по умолчанию; для очень длинной строки настройте буфер через Buffer до начала чтения или выберите Reader.

Копирование и вывод

Функция io.Copy(dst, src) читает из источника и записывает в назначение до конца или ошибки. В программе строковый Reader выступает источником, а bytes.Buffer — Writer. Вызов io.Copy корректно обрабатывает короткие операции на пути копирования и возвращает число переданных байтов. Для настоящего файла после успешного os.Open обычно регистрируют defer file.Close(); ошибку открытия проверяют до вызова методов файла.

Практика

Попробуйте сами

Создайте программу, которая читает из strings.NewReader текст "чай\nкофе\nкакао\n" через bufio.Scanner. Форматируйте каждую строку с номером в strings.Builder через fmt.Fprintf, проверяя ошибку записи, а затем передайте собранный отчёт в io.Copy с os.Stdout как назначением. Проверьте Scanner.Err и ошибку io.Copy.

Проверка результата

Как понять, что получилось

В терминале ровно три строки отчёта: 1: чай, 2: кофе, 3: какао. После них нет лишней пустой строки; ошибки Scanner и io.Copy проверяются.

Потоки чтения и записи | Go | WebSchool · Go