Go: 标准库 io

2022-02-222022-02-223 引用

最基础的 I/O 抽象层,核心作用是把"读、写"等能力统一成一组通用接口。

io 包定义了接口和组合方式,不负责具体实现。

核心接口

提供读、写、关闭、定位四个基础能力;这四个基础能力可以按需组合成更具体的接口

写数据

w io.Writer看作数据目标,n, err = w.Write(p)表示把p里的数据n个bytew

语义:流式生产,向当前位置顺序写;返回实际写入的字节数 n

go
// go doc io.Writer
type Writer interface {
	Write(p []byte) (n int, err error)
}

读数据

r io.Reader看作数据源,n, err = r.Read(p)表示从数据源r中读取n个byte缓存区p中;

语义:流式消费,从当前位置顺序读;最多读 len(p) 个字节,不保证读满,返回实际读到的字节数 n

go
// go doc io.Reader
type Reader interface {
	Read(p []byte) (n int, err error)
}

疑问:为什么不 Read() (p []byte, err error)?

回答:由调用方决定[]byte,方便复用,减少内存压力

关闭

go
// go doc io.Closer
type Closer interface {
	Close() error
}

定位

设置读/写操作的当前位置偏移量

go
// 第二个参数表示位置是开始/当前/末尾
// go doc io.Seeker
type Seeker interface {
	Seek(offset int64, whence int) (int64, error)
}
 
// Seek whence values.
const (
	SeekStart   = 0 // seek relative to the origin of the file
	SeekCurrent = 1 // seek relative to the current offset
	SeekEnd     = 2 // seek relative to the end
)

一些变体(特殊语义)

数据搬运优化

谁来主导数据搬运,以及能不能走优化路径,比如 sendfile、splice、内部缓冲优化

go
// 目标端主动从源端r拉取数据
// go doc io.ReaderFrom
type ReaderFrom interface {
	ReadFrom(r Reader) (n int64, err error)
}
 
// 源端主动向目标端w推数据
// go doc io.WriterTo
type WriterTo interface {
	WriteTo(w Writer) (n int64, err error)
}

src/os/zero_copy_linux.go:18 *os.File 实现的零拷贝优化

按位置访问

可按位置访问的数据空间

go
// go doc io.ReaderAt
type ReaderAt interface {
	ReadAt(p []byte, off int64) (n int, err error)
}
 
// go doc io.WriterAt
type WriterAt interface {
	WriteAt(p []byte, off int64) (n int, err error)
}

适配器(组合)

把已有的 Reader/Writer 重新组合成新的行为

go
// 返回一个新的 Writer,写一次,相当于同时写多个目标
// go doc io.MultiWriter
func MultiWriter(writers ...Writer) Writer
 
// 返回一个新的 Reader,按顺序读多个输入源
// go doc io.MultiReader
func MultiReader(readers ...Reader) Reader
 
// 返回一个新的 Reader,读取时同时把读到的数据写进 w
// go doc io.TeeReader
func TeeReader(r Reader, w Writer) Reader
 
// 返回一个新的 Reader,但最多只能读 n 字节
// go doc io.LimitReader
func LimitReader(r Reader, n int64) Reader
 
// 返回一个新的 ReadCloser,Close() 是空实现
// go doc io.NopCloser
func NopCloser(r Reader) ReadCloser

工具函数

  1. 读数据
go
// go doc io.ReadAll
func ReadAll(r Reader) ([]byte, error)
 
// 读入数据到buf,读满
func ReadFull(r Reader, buf []byte) (n int, err error)
 
// 读入数据到buf,读至少min个字节
func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)
  1. 写数据
go
// go doc io.WriteString
func WriteString(w Writer, s string) (n int, err error)
  1. 复制数据
go
// go doc io.Copy
// I/O 里最核心的"数据搬运调度器"
func Copy(dst Writer, src Reader) (written int64, err error)
 
// 只拷贝N个字节
func CopyN(dst Writer, src Reader, n int64) (written int64, err error)
 
// 缓存区caller提供
func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error)
  1. 管道
go
// go doc io.Pipe
// io.Pipe 是同步内存管道。没有内部 buffer。写端会阻塞,直到读端把这次写入的数据读走。
func Pipe() (*PipeReader, *PipeWriter)

错误、值

bash
# 正常读到结尾
go doc io.EOF
 
# 本来还该有数据,但提前结束
go doc io.ErrUnexpectedEOF
 
# 写入不完整
go doc io.ErrShortWrite
 
# 缓冲区太小
go doc io.ErrShortBuffer
 
# 对已关闭的管道读写
go doc io.ErrClosedPipe
 
# 多次 Read 都没有进展
go doc io.ErrNoProgress
 
# 值:类“黑洞”,写进去的数据直接丢弃
# var Discard Writer = discard{}
go doc io.Discard

评论