0Pricing
Go Academy · Урок

Реализация собственных средств чтения

Создавайте собственные потоки

«Реализация собственных средств чтения» — бесплатный урок Go Academy на CoddyKit. Это урок 4 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Go Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Go Academy содержит 4 уроков всего.

Создание собственных потоков

Вы можете создавать собственные источники данных, реализовав интерфейс io.Reader с единственным методом. После этого ваш тип будет работать с любым вспомогательным средством io.

Реализуемый метод

Реализуйте Read(p []byte) (n int, err error) для своего типа. Заполните p, верните количество байтов и верните io.EOF, когда данные закончатся.

Читатель константного значения

Здесь показан читатель, бесконечно выдающий один и тот же байт, ограниченный с помощью LimitReader.

package main

import (
	"fmt"
	"io"
)

type constReader struct{ ch byte }

func (c constReader) Read(p []byte) (int, error) {
	for i := range p {
		p[i] = c.ch
	}
	return len(p), nil
}

func main() {
	r := io.LimitReader(constReader{ch: 'A'}, 5)
	data, _ := io.ReadAll(r)
	fmt.Println(string(data))
}

Сигнализация EOF

Верните io.EOF, когда источник будет исчерпан. Можно вернуть его отдельно или вместе с последними байтами.

package main

import (
	"fmt"
	"io"
)

type oneShot struct{ done bool }

func (o *oneShot) Read(p []byte) (int, error) {
	if o.done {
		return 0, io.EOF
	}
	o.done = true
	n := copy(p, []byte("once"))
	return n, nil
}

func main() {
	data, _ := io.ReadAll(&oneShot{})
	fmt.Println(string(data))
}

Чтение из среза

Собственный читатель может отслеживать позицию в исходных данных и возвращать при каждом вызове очередную часть.

package main

import (
	"fmt"
	"io"
)

type sliceReader struct {
	data []byte
	pos  int
}

func (s *sliceReader) Read(p []byte) (int, error) {
	if s.pos >= len(s.data) {
		return 0, io.EOF
	}
	n := copy(p, s.data[s.pos:])
	s.pos += n
	return n, nil
}

func main() {
	r := &sliceReader{data: []byte("custom")}
	out, _ := io.ReadAll(r)
	fmt.Println(string(out))
}

Преобразующий читатель

Оберните другой читатель и преобразуйте байты на лету — здесь каждый байт переводится в верхний регистр по мере чтения потока.

package main

import (
	"bytes"
	"fmt"
	"io"
)

type upper struct{ r io.Reader }

func (u upper) Read(p []byte) (int, error) {
	n, err := u.r.Read(p)
	for i := 0; i < n; i++ {
		if p[i] >= 'a' && p[i] <= 'z' {
			p[i] -= 32
		}
	}
	return n, err
}

func main() {
	out, _ := io.ReadAll(upper{r: bytes.NewReader([]byte("hello"))})
	fmt.Println(string(out))
}

Учитывайте длину буфера

Никогда не записывайте больше len(p) байтов. Используйте copy(p, src): число копируемых байтов ограничено меньшим из срезов, а затем верните это число.

Получатели-значения и получатели-указатели

Если ваш читатель отслеживает позицию или состояние, используйте получатель-указатель, чтобы изменения сохранялись между вызовами. Для читателей без состояния можно использовать получатель-значение.

Композиция с помощниками io

Поскольку ваш тип соответствует io.Reader, его можно сразу использовать с io.Copy, bufio.NewReader, io.LimitReader и другими средствами.

package main

import (
	"io"
	"os"
)

type dots struct{ left int }

func (d *dots) Read(p []byte) (int, error) {
	if d.left == 0 {
		return 0, io.EOF
	}
	p[0] = '.'
	d.left--
	return 1, nil
}

func main() {
	io.Copy(os.Stdout, &dots{left: 4})
	os.Stdout.WriteString("\n")
}

Распространённые ошибки

  • Забыть вернуть io.EOF — чтение станет бесконечным
  • Записать данные за пределами len(p)
  • Использовать получатель-значение для читателей с состоянием

Зачем нужны собственные читатели

Они позволяют встроить любой источник — генераторы, декодеры, ограничители скорости, зашифрованные потоки — в универсальную экосистему ввода-вывода Go.

Быстрая проверка

Проверьте свои знания о собственных читателях.

Итоги

Вы научились создавать собственные потоки.

  • Реализуйте Read(p []byte) (int, error)
  • Используйте copy и учитывайте len(p)
  • Возвращайте io.EOF, когда данные закончатся
  • Для читателей с состоянием используйте получатель-указатель
  • Свободно объединяйте их со всеми вспомогательными средствами io

Часто задаваемые вопросы

Урок «Реализация собственных средств чтения» бесплатный?

Да — полный текст урока «Реализация собственных средств чтения» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Go Academy, подпишись на CoddyKit PRO. Курс Go Academy содержит 4 уроков всего.

Чему я научусь в уроке «Реализация собственных средств чтения»?

Создавайте собственные потоки Ты практикуешь Go Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Go Academy?

Предыдущий опыт не требуется. Go Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 4 из 4.

Сколько времени занимает урок «Реализация собственных средств чтения»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Go Academy?

Да. Каждый урок Go Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Интерфейс Reader
  2. Интерфейс Writer
  3. Вспомогательные функции io
  4. Реализация собственных средств чтения
← Назад к Go Academy