io.Reader 与 io.Writer
本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 73 课,共 107 课。
Go 的标准库通过两个基本接口展示了基于接口的设计:io.Reader 和 io.Writer。这些简单的接口支撑着从文件操作到网络通信的各种功能。
io.Reader 接口只要求实现一个方法:
type Reader interface {
Read(p []byte) (n int, err error)
}它最多将 len(p) 个字节读入切片,并返回已读取的字节数。类似地,io.Writer 定义了:
type Writer interface {
Write(p []byte) (n int, err error)
}实现这些方法的任何类型都能与整个 I/O 生态系统配合使用。
这是一个实现了 io.Reader 的自定义类型:
type RepeatReader struct {
Char byte
Count int
pos int
}
func (r *RepeatReader) Read(p []byte) (int, error) {
if r.pos >= r.Count {
return 0, io.EOF
}
n := 0
for n < len(p) && r.pos < r.Count {
p[n] = r.Char
n++
r.pos++
}
return n, nil
}现在,这个自定义读取器可以与任何需要 io.Reader 的函数配合使用:
reader := &RepeatReader{Char: 'A', Count: 5}
data, _ := io.ReadAll(reader)
fmt.Println(string(data)) // AAAAA这种设计让你可以编写接收 io.Reader 或 io.Writer 的函数,使它们能够处理文件、网络连接、缓冲区或任何自定义实现。
挑战
简单一起来构建一个自定义文本流处理器,实现 io.Reader 和 io.Writer 接口吧!你将创建一些类型,在文本流经它们时对文本进行转换,展示这些基础接口如何实现强大的 I/O 组合。
你需要将代码分布在两个文件中:
streams.go:定义自定义的读取器和写入器类型。创建一个
CountingReader结构体,它包装一个字符串,并跟踪已读取的字节数。它应该有用于存储源字符串和跟踪位置的字段。实现Read(p []byte) (int, error)方法,从源中读取字节并放入提供的切片,更新位置,并在源数据耗尽时返回io.EOF。创建一个
UppercaseWriter结构体,它会收集写入的数据并将其转换为大写。它应该在内部存储累积的结果。实现Write(p []byte) (int, error)方法,将传入的字节转换为大写并存储。添加一个Result() string方法,用于获取累积的大写文本。创建构造函数
NewCountingReader(source string) *CountingReader和NewUppercaseWriter() *UppercaseWriter,以正确初始化这些类型。main.go:演示如何让你的自定义 I/O 类型与标准库配合使用。读取一个字符串输入,然后使用
CountingReader配合io.ReadAll读取所有数据。通过UppercaseWriter的Write方法,将这些数据传入其中。按以下格式输出结果:
Original: [input] Uppercase: [result from writer] Bytes read: [total bytes]
将提供以下输入:
- 一行文本
例如,给定:
Hello World你的输出应为:
Original: Hello World
Uppercase: HELLO WORLD
Bytes read: 11再例如,给定:
Go interfaces are powerful你的输出应为:
Original: Go interfaces are powerful
Uppercase: GO INTERFACES ARE POWERFUL
Bytes read: 26关键在于,CountingReader 实现了 io.Reader,因此可以与 io.ReadAll 无缝配合。在 Go 生态中,任何接受 io.Reader 的函数都能与你的自定义类型配合使用,这正是基于接口设计的强大之处。
亲手试试
package main
import (
"bufio"
"fmt"
"io"
"os"
)
func main() {
// 读取输入
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
// 如果末尾有换行符,就将其删除
if len(input) > 0 && input[len(input)-1] == '\n' {
input = input[:len(input)-1]
}
// TODO: 用输入字符串创建 CountingReader
// countingReader := NewCountingReader(input)
// TODO: 使用 io.ReadAll 读取你的 CountingReader 中的所有数据
// data, err := io.ReadAll(countingReader)
// TODO: 创建 UppercaseWriter
// upperWriter := NewUppercaseWriter()
// TODO: 将数据写入你的 UppercaseWriter
// upperWriter.Write(data)
// TODO: 按要求的格式打印结果:
// 原文:[input]
// 大写:[writer 的结果]
// 已读取字节数:[总字节数]
// 占位代码,用于避免未使用的导入错误;实现时请删除
_ = io.EOF
fmt.Println("Original:", input)
// fmt.Println("Uppercase:", upperWriter.Result())
// fmt.Println("Bytes read:", countingReader.Position)
}
这节课包含一个小测验。开始学习即可作答,并记录你的进度。
面向对象编程中的所有课
11标准库与面向对象编程
io.Reader 与 io.Writersort.Interfacefmt.Stringer 接口使用 encoding/json 处理结构体http.Handler 接口回顾:REST API 模型自己练习: Go 在线编译器