Go 标准库学习:io
二月其一,学习 Go 的 io 包和 Buffer 结构体(Go 版本 1.16.5)。
io 包定义了 Go 的 IO 接口,Buffer 结构体实现了其中大部分接口。明确一下代码位置:io 包下的 io.go
文件,以及 bytes 包下的 buffer.go
文件。
学习过程中,主要参考博文《Golang学习 - io 包》,写得很好。
io 包
io 包定义了很多 interface,每个 interface 定义了一个基本的方法,没有实现,例如:
1 | type Reader interface { |
像 Reader
这样的 interface 有很多,类似的还有 Writer
、ReaderFrom
、WriterTo
等等。
io 包由这样一个个的 interface 组成,这些 interface 定义了 Go 语言中的 IO 标准(只是单纯地定义),本身没有实现,具体怎么实现,交给别的包。
Package io provides basic interfaces to I/O primitives. Its primary job is to wrap existing implementations of such primitives, such as those in package os, into shared public interfaces that abstract the functionality, plus some other related primitives.
Because these interfaces and primitives wrap lower-level operations with various implementations, unless otherwise informed clients should not assume they are safe for parallel execution.
————————
io 包提供了 I/O 原语的基本接口。它的首要任务是提供 io 原语的包装实现,例如把 os 包中的 io 原语,转换成公共抽象接口,并增加一些相关原语。
因为这些接口和原语包装的都是原始操作,且实现众多,因此除非有声明,否则使用时不应假设它们并发安全。
Buffer 结构体
buffer.go
文件的注释非常简单:
Simple byte buffer for marshaling data.
————————
用于整理(marshal)数据的简单字节缓冲区。
Buffer 结构体(struct)实现了很多 io 基本接口,而且实现简单,因此我们通过 Buffer 结构体来学习 io 包。
比如 io 包定义了 Reader 接口及其内部的 Read 方法,而 Buffer 就实现了 Read 方法:
1 | buffer := bytes.NewBufferString("abc") |
io API
下表整理了一下,io 包(准确的说是 io.go
文件中)都定义了哪些 interface 和方法,以及 Buffer 结构体实现了哪些部分。
io interface | 嵌套 interface | func | 功能 | buffer 是否实现 |
---|---|---|---|---|
Reader | Read(p []byte) (n int, err error) | 用于输出自身的数据(-> p) | ✅ | |
Writer | Write(p []byte) (n int, err error) | 用于将数据存入自身(p ->) | ✅ | |
Closer | Close() error | 用于关闭数据读写(关闭文件、通道、连接、数据库……) | ||
Seeker | Seek(offset int64, whence int) (int64, error) | 用于移动数据的读写指针(每一次读写操作都从指针位置开始) | ||
ReadWriter | Reader Writer |
组合接口 | ✅ | |
ReadCloser | Reader Closer |
组合接口 | ||
WriteCloser | Writer Closer |
组合接口 | ||
ReadWriteCloser | Reader Writer Closer |
组合接口 | ||
ReadSeeker | Reader Seeker |
组合接口 | ||
ReadSeekCloser | Reader Seeker Closer |
组合接口 | ||
WriteSeeker | Writer Seeker |
组合接口 | ||
ReadWriteSeeker | Reader Writer Seeker |
组合接口 | ||
ReaderFrom | ReadFrom(r Reader) (n int64, err error) | 用于从 r 中读取数据存入自身 | ✅ | |
WriterTo | WriteTo(w Writer) (n int64, err error) | 用于将自身的数据写入 w 中 | ✅ | |
ReaderAt | ReadAt(p []byte, off int64) (n int, err error) | 用于从指定偏移位置开始,输出自身的数据(-> p) | ||
WriterAt | WriteAt(p []byte, off int64) (n int, err error) | 用于从指定偏移位置开始,将数据存入自身(p ->) | ||
ByteReader | ReadByte() (byte, error) | 用于从自身读出一个字节 | ✅ | |
ByteScanner | ByteReader | UnreadByte() error | 用于从自身读出一个字节,且可以撤销最后一次读取(下次可以读出一样的数据) | ✅ |
ByteWriter | WriteByte(c byte) error | 用于将一个字节写入自身 | ✅ | |
RuneReader | ReadRune() (r rune, size int, err error) | 用于从自身读取一个 UTF-8 编码的字符到 r 中 | ✅ | |
RuneScanner | RuneReader | UnreadRune() error | 用于从自身读取一个 UTF-8 编码的字符到 r 中,且可以撤销最后一次读取(下次可以读出一样的数据) | ✅ |
StringWriter | WriteString(s string) (n int, err error) | 用于将字符串 s 写入到 w 中 | ✅ |
可以看出,Buffer 实现了其中大部分的接口。
下面列出 Buffer 实现的全部 io 方法:
1 | buffer := bytes.NewBuffer([]byte{}) // 下文注释中,buffer实例内部的存储内存,用【自身🗂️】表示 |
Buffer 实现
Buffer 结构体的设计很简单:
1 | type Buffer struct { |
Buffer 结构体内部只有三个变量:
byte 数组,用于存放数据
下次读缓存的起始下标(下一次调用
Read
方法时,读缓存的起点是buf[off]
)枚举:上次使用 Read 方法,最后读到了什么(用于
Read
之后Unread
撤回)有六种,分别是【从没读过】、【读了长 1 byte 的字符】、【读了长 2 byte 的字符】、【读了长 3 byte 的字符】、【读了长 4 byte 的字符】、【读了其他长度的字符】
源码就不分析了。
文章《Golang bytes.Buffer 用法精述》总结了 Buffer 的常用方法(部分跟上文重复),这里拷贝如下:
往 Buffer 中写入数据:
1 | b.Write(d []byte) (n int, err error) // 将切片d写入Buffer尾部 |
从 Buffer 中读取数据:
1 | b.Next(n int) []byte // 读取 n 个字节数据并返回,如果 buffer 不足 n 字节,则读取全部 |
其他操作:
1 | b.Bytes() []byte // 返回字节切片 |
ioutil
还有一个 ioutil 包,用来提供 io 相关的工具方法。
在 Go 1.16 版本之后,ioutil 包的各个方法,基本都被其他包重新实现了,例如 ioutil.ReadDir
推荐替换成 os.ReadDir
。
ioutil 包下的方法整理如下:
ioutil 方法 | 功能 | Go 1.16 之后推荐替代方法 |
---|---|---|
ReadAll(r io.Reader) ([]byte, error) | 从 io.Reader 中读取全部数据,返回 []byte | io.ReadAll |
ReadDir(dirname string) ([]fs.FileInfo, error) | 读取目录下的所有文件(向下一级) | os.ReadDir |
ReadFile(filename string) ([]byte, error) | 读取文件,返回 []byte | os.ReadFile |
WriteFile(filename string, data []byte, perm fs.FileMode) error | 把 []byte 写到文件 | os.WriteFile |
NopCloser(r io.Reader) io.ReadCloser | 把 io.Reader 包装成 io.ReadCloser | io.NopCloser |
TempDir(dir, pattern string) (name string, err error) | 创建临时目录 | – |
TempFile(dir, pattern string) (f *os.File, err error) | 创建临时文件 | – |