0Pricing
Go Academy · 课时

实现自定义读取器

构建您自己的流

实现自定义读取器 是 CoddyKit 上的免费 Go Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 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 I/O 生态系统中。

快速检查

请测试您对自定义读取器的了解。

回顾

您学习了如何构建自己的数据流。

  • 实现 Read(p []byte) (int, error)
  • 使用 copy 并遵守 len(p) 的限制
  • 完成时返回 io.EOF
  • 有状态读取器使用指针接收者
  • 可以直接与所有 io 辅助工具组合

常见问题解答

「实现自定义读取器」课时是免费的吗?

是的 — 「实现自定义读取器」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。

「实现自定义读取器」这节课中我会学到什么?

构建您自己的流 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Go Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。

「实现自定义读取器」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Go Academy 课中编写并运行代码吗?

能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Reader 接口
  2. Writer 接口
  3. io 工具函数
  4. 实现自定义读取器
← 返回 Go Academy