你必须非常努力,才能看起来毫不费力!
微信搜索公众号[ 漫漫Coding路 ],一起From Zero To Hero !
前言
IO
操作是我们在编程中不可避免会遇到的,例如读写文件,Go语言的 io 包中提供了相关的接口,定义了相应的规范,不同的数据类型可以根据规范去实现相应的方法,提供更加丰富的功能。
Go
语言提倡小接口
+ 接口组合
的方式,来扩展程序的行为以及增加程序的灵活性。io代码包恰恰就可以作为这样的一个标杆,它可以成为我们运用这种技巧时的一个参考标准。
上一篇文章,我们学习了io包中的四个核心接口,本篇文章我们来学习下多个基本接口!
ReaderFrom
io.ReaderFrom 接口定义了 ReadFrom
方法,用于从一个 Reader 中读取数据
- 入参:Reader r
- 返回值:n为读取的字节数,err 为读取过程中遇到的error
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}
方法功能详解
- 该方法从 Reader r 中读取数据,直到读到文件末尾(EOF)或者 遇到 其他 error
- 返回的 n 就是读取的字节数
WriterTo
io.WriteTo 接口定义了 WriteTo
方法,用于将数据写入到一个 Writer 中
- 入参:Writer w
- 返回值:n为写入的字节数,err 为写入过程中遇到的 error
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
方法功能详解
- 该方法将数据写入到 w 中,直到没有数据可写或者遇到error
- 返回的 n 就是写入的字节数
ReaderAt
io.ReaderAt 接口定义了 ReadAt
方法,用于从指定位置读取数据
- 入参:读取的起始位置 off,读取后写入字节数组 p
- 返回值:n 为成功读取的字节数,err为读取过程中遇到的 error
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}
方法功能详解
- 方法从指定的偏移量位置 off 开始读取指定的数据,然后至多读取 len(p) 个字节,写入字节数组 p
- 返回成功读取的字节量 n,以及遇到的 error
- 当该方法返回的 n < len(p) 时,即读取的数据没有将字节数组p填充满,返回的 err != nil,在这一点上,ReadAt方法要比 Read方法严格(Read方法规定遇到n<len(p),本次调用可以返回 err=nil,然后在下一次调用时返回err!=nil )
- 即使方法返回的 n < len(p),也可能会占用整个字节数组 p 作为暂存空间
- 如果数据可用,但是不够 len(p)个字节,ReadAt 方法会阻塞,直到所有数据可用 或者 遇到 error,这一点也与 Read 方法不同
- 如果方法返回的 n=len(p),并且此时正好读取到了文件末尾,ReadAt 可以返回 err = EOF 或者 err = nil。如果返回的 err 为 nil的话,下次就一定返回 EOF 了。
不能影响seek offset
- 如果 ReadAt 方法的入参 off 是由 seek 方法计算得到的,ReadAt 方法不能修改 off 的值
可并行
- 对于同一个数据源,可以并行调用 ReadAt 方法读取数据
方法实现需注意
- 实现该方法后,一定不要持有字节数组p(不能保存 p 的地址,用于其他地方)
WriteAt
io.WriterAt 接口定义了 WriteAt
方法,将字节数组 p 中的数据,从文件 off 偏移量开始写入到文件中
- 入参:字节数组p,文件开始写处的偏移量 off
- 返回值:成功写入的字节数 n,写入过程中遇到的error err
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}
方法功能详解
- 方法将 字节数组 p 中的 len(p) 个字节,从文件 off 位置开始,写入到文件中。
- 方法返回成功写入的字节个数n (0 <= n <= len(p)) ,以及是否遇到 error
- 如果 n < len(p),方法必须返回 err!=nil
不能影响offset
- 如果 WriteAt 方法的入参 off 是由 seek 方法计算得到的,ReadAt 方法不能修改 off 的值
可并行
- 对于同一个数据源,如果写入的范围不重叠,可以并行调用 WriteAt 方法写数据
方法实现需注意
- 实现该方法后,一定不要持有字节数组p(不能保存 p 的地址,用于其他地方)
ByteReader
io.ByteReader 接口定义了 ReadByte
方法,用于读取一个字节
- 返回值:读取的一个字节,以及可能遇到的 error
type ByteReader interface {
ReadByte() (byte, error)
}
方法功能详解
- 方法读取底层数据流的下一个字节,然后返回该字节,同时返回对应的error
- 如果方法读取过程中遇到error,不会消费底层数据的一个字节,相当于当前读取的位置不会变
ByteScanner
ByteScanner 是 ByteReader
接口 和 UnreadByte
方法的组合,因此该组合接口包含了两个方法,一个 ReadByte,一个 UnreadByte。从名字我们也可以看出来,ReadByte 是读取一个字节,UnreadByte 是回退一个字节。
type ByteScanner interface {
ByteReader
UnreadByte() error
}
方法功能详解
- 在调用一次 ReadByte 之后,如果调用一次 UnreadByte,那么下次再调用 ReadByte 方法得到的值,和第一次调用 ReadByte 应该一样,也就是回退了一个位置。
- 如果连续两次调用 UnreadByte,中间没有调用 ReadByte,可能会产生 error (这里说的可能,比如从文件开始读取一次,然后两次回退,那么回退到的位置可能就不合法了,具体还是依赖实现方法定义)
ByteWriter
io.ByteWriter 接口定义了 WriteByte
方法,用于向底层文件写入一个字节,然后返回写入过程产生的 error
type ByteWriter interface {
WriteByte(c byte) error
}
RuneReader
io.RuneReader 接口定义了 ReadRune
方法,读取一个 UTF-8 字符。UTF-8 是一种变长编码规则,从 1 到 4 个字节不等,比如一个字母占一个字节,中文每个字符占用 3 个字节。ReadRune 每次读出的是一个完整的编码字符,比如读汉字,每次就会读出一个汉字。
type RuneReader interface {
ReadRune() (r rune, size int, err error)
}
RuneScanner
类似 ByteScanner,RuneScanner
也是个组合接口,提供了 UnreadRune 方法,可以回退一个 rune。如果连续两次调用 UnreadRune 方法,可能会产生error。
type RuneScanner interface {
RuneReader
UnreadRune() error
}
StringWriter
io.StringWriter 接口定义了 WriteString
方法,用于将字符串 s 写入底层的数据流中,返回写入成功的字节数 n,以及可能产生的 error
// StringWriter is the interface that wraps the WriteString method.
type StringWriter interface {
WriteString(s string) (n int, err error)
}
总结
本篇文章介绍了 io包 中的十个基本接口及对应的方法:
- ReaderFrom: 定义了 ReadFrom 方法,从 Reader 中读取数据
- WriterTo: 定义了 WriteTo 方法,向 Writer 中写入数据
- ReaderAt: 定义了 ReadAt 方法,从指定位置读取数据
- WriterAt: 定义了 WriteAt 方法,从指定位置写入数据
- ByteReader: 定义了 ReadByte 方法,读取一个字节
- ByteScanner: 组合接口,除了ReadByte 方法,定义了 UnreadByte 方法,用于回退一个字节
- ByteWriter: 定义了 WriteByte 方法,向文件写入一个字节
- RuneReader: 定义了 ReadRune 方法,读取一个 UTF-8 字符
- RuneScanner: 组合接口,除了 ReadRune 方法,定义了 UnreadRune 方法,用于回退一个 rune
- StringWriter: 定义了 WriteString 方法,用于向文件写入一个字符串
更多
个人博客: https://lifelmy.github.io/
微信公众号:漫漫Coding路