Golang基本输入输出(io包接口)
lookupman Lv2

  在本章的所有的io接口时,有一个特点,在读取或写入的n=len(p)时,返回的接口err=nil或者err=EOF;当n<len(p)时,需要返回err解释原因。

1. Reader接口

  在官方io包中定义了如下接口,只要实现了Read方法,就是将其传递为io.Reader类型的参数

1
2
3
type Reader interface {
Read(p []byte)(n int, err error)
}

2. Writer接口

  在官方的io包中定义里如下接口,只要实现了Write方法,也就是实现了Writer接口

1
2
3
type Writer interface{
Write(p []byte)(n int, err error)
}

3. 实现Reader和Writer接口的类型

  • os.File同时实现了io.Reader和io.Writer接口
  • strings.Reader实现了io.Reader接口
  • bufio.Reader/Writer分别实现了io.Reader和io.Writer接口
  • bytes.Buffer同时实现了io.Reader和io.Writer接口
  • bytes.Reader实现了io.Reader接口
  • compress/gzip.StreamReader/StreamWriter分别实现了io.Reader和io.Writer接口
  • encoding/csv.Reader/Writer分别实现了io.Reader和io.Writer接口
  • net/conn分别实现了io.Reader和io.Writer接口(Conn接口定义了Read/Write)

    4. ReaderAt接口

      该接口是从源输入的偏移量off处读取len(p)个字节到p中。
    1
    2
    3
    type ReaderAt struct {
    ReadAt(p []byte, off int64)(n int, err error)
    }

    5. WriterAt接口

      该接口是在源输出的偏移量off处写入len(p)个字节。
    1
    2
    3
    type WriterAt struct {
    WriteAt(p []byte, off int64)(n int, err error)
    }

    6. ReaderFrom接口

      ReadFrom从r中读取数据,直到EOF或发生错误。
    1
    2
    3
    type ReaderFrom struct {
    ReadFrom(r Reader)(n int64, err error)
    }
    使用示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    file, err := os.Open("writerAt.txt")
    if err != nil{
    painc(err)
    }
    defer file.Close()

    writer := bufio.NewWriter(os.Stdout)
    writer.ReadFrom(file)
    writer.Flush()

    7. WriterTo接口

     emsp;WriteTo 将数据写入 w 中,直到没有数据可写或发生错误。
    1
    2
    3
    type WriterTo struct{
    WriteTo(w Writer)(n int, err error)
    }

    8. Seeker接口

      Seek 设置下一次 Read 或 Write 的偏移量为 offset,它的解释取决于 whence: 0 表示相对于文件的起始处,1 表示相对于当前的偏移,而 2 表示相对于其结尾处。 Seek 返回新的偏移量和一个错误,如果有的话。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    const(
    SeekStart = 0
    SeekCurrent = 1
    SeekEnd = 2
    )

    type Seeker struct{
    Seek(offset int64, whence int)(ret int64, err error)
    }

    9. Closer接口

    接口定义如下:
    1
    2
    3
    type Closer interface{
    Close() error
    }

10. ByteReader和ByteWriter接口

  这个两个接口用于读/写一个字节

1
2
3
4
5
6
7
type ByteReader interface{
ReadByte()(c byte, err error)
}

type ByteWriter interface{
WriteByte(c byte) error
}

在标准库中,有如下类型实现了 io.ByteReader 或 io.ByteWriter:
bufio.Reader/Writer 分别实现了io.ByteReader 和 io.ByteWriter
bytes.Buffer 同时实现了 io.ByteReader 和 io.ByteWriter
bytes.Reader 实现了 io.ByteReader
strings.Reader 实现了 io.ByteReader

11. ByteScanner、RuneReader和RuneScanner接口

  UnreadByte 方法的意思是:将上一次 ReadByte 的字节还原,使得再次调用 ReadByte 返回的结果和上一次调用相同,也就是说,UnreadByte 是重置上一次的 ReadByte。注意,UnreadByte 调用之前必须调用了 ReadByte,且不能连续调用 UnreadByte。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type ByteScanner interface {
ByteReader
UnreadByte() error
}

//RuneReader和ByteReader类似
type RuneReader interface {
ReadRune()(r rune, size int, err error)
}

//RuneScanner和ByteScanner类似
type RuneScanner interface {
RuneReader
UnreadRune() error
}

12. SectionReader接口

  NewSectionReader 返回一个 SectionReader,它从 r 中的偏移量 off 处读取 n 个字节后以 EOF 停止。也就是说,SectionReader 只是内部(内嵌)ReaderAt 表示的数据流的一部分:从 off 开始后的 n 个字节。
这个类型的作用是:方便重复操作某一段 (section) 数据流;或者同时需要 ReadAt 和 Seek 的功能。

1
2
3
4
5
6
7
8
9
10
type SectionReader struct {
r ReaderAt
base int64 //NewSectionReader会将base设置为off
off int64 //从r中off偏移处开始读取数据
limit int64 //limit-off = SectionReader流的长度
}

func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {

}
  1. LimitedReader接口
      从 R 读取但将返回的数据量限制为 N 字节。每调用一次 Read 都将更新 N 来反应新的剩余数量。也就是说,最多只能返回 N 字节数据。
    1
    2
    3
    4
    type LimitedReader struct {
    R Reader //underlying reader,最终的读取通过R.Read完成
    N int64 //max bytes remaining
    }
      使用示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    content := "This is LimitReader Example"
    reader := strings.NewReader(content)
    limitReader := &io.LimitedReader{R:reader, N:8}
    for limitReader.N > 0{
    tmp := make([]byte, 2)
    limitReader.Read(tmp)
    fmt.Printf("%s", tmp)
    }

    //输出: This Is
    //从结果看一看出最多只能读取N个字符

14. PipeReader和PipeWriter接口

  关于 PipeReader.Read 方法的说明:从管道中读取数据。该方法会堵塞,直到管道写入端开始写入数据或写入端被关闭。如果写入端关闭时带有 error(即调用 CloseWithError 关闭),该Read返回的 err 就是写入端传递的error;否则 err 为 EOF。
  关于 PipeWriter.Write 方法的说明:写数据到管道中。该方法会堵塞,直到管道读取端读完所有数据或读取端被关闭。如果读取端关闭时带有 error(即调用 CloseWithError 关闭),该Write返回的 err 就是读取端传递的error;否则 err 为 ErrClosedPipe。

1
2
3
4
5
6
7
type PipeReader struct {
p *pipe
}

type PipeWriter struct{
p *pipe
}

  使用示例如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func main() {
pipeReader, pipeWriter := io.Pipe()
go PipeWrite(pipeWriter)
go PipeRead(pipeReader)
time.Sleep(30 * time.Second)
}

func PipeWrite(writer *io.PipeWriter){
data := []byte("hello world!")
for i := 0; i < 3; i++{
n, err := writer.Write(data)
if err != nil{
fmt.Println(err)
return
}
fmt.Printf("写入字节%d\n", n)
}
writer.CloseWithError(errors.New("写入段已经关闭!"))
}

func PipeRead(reader *io.PipeReader){
buf := make([]byte, 128)
for{
fmt.Println("接口开始测试....")
time.Sleep(5*time.Second)
n, err := reader.Read(buf)
if err != nil{
fmt.Println(err)
return
}
fmt.Printf("收到%d个字节,数据为:%s\n", n, buf)
}
}

15. Copy和CopyN函数

  Copy 将 src 复制到 dst,直到在 src 上到达 EOF 或发生错误。它返回复制的字节数,如果有错误的话,还会返回在复制时遇到的第一个错误。成功的 Copy 返回 err == nil,而非 err == EOF。由于 Copy 被定义为从 src 读取直到 EOF 为止,因此它不会将来自 Read 的 EOF 当做错误来报告。
  若 dst 实现了 ReaderFrom 接口,其复制操作可通过调用 dst.ReadFrom(src) 实现。此外,若 src 实现了 WriterTo 接口,其复制操作可通过调用 src.WriteTo(dst) 实现。

1
func Copy(dst Writer, src Reader)(written int64, err error){}

  CopyN 将 n 个字节(或到一个error)从 src 复制到 dst。 它返回复制的字节数以及在复制时遇到的最早的错误。当且仅当err == nil时,written == n 。
  若 dst 实现了 ReaderFrom 接口,复制操作也就会使用它来实现。

1
func CopyN(dst Writer, src Reader, n int64)(written int64, err error){}

16. ReadAtLeast和ReadFull函数

  ReadAtLeast 将 r 读取到 buf 中,直到读了最少 min 个字节为止。它返回复制的字节数,如果读取的字节较少,还会返回一个错误。若没有读取到字节,错误就只是 EOF。如果一个EOF 发生在读取了少于min个字节之后,ReadAtLeast就会返回ErrUnexpectedEOF。若 min 大于 buf 的长度,ReadAtLeast 就会返回ErrShortBuffer。对于返回值,当且仅当 err == nil 时,才有 n >= min。

1
func ReadAtLeast(r Reader, buf []byte, min int)(n int, err error){}

  ReadFull 精确地从 r 中将 len(buf) 个字节读取到 buf 中。它返回复制的字节数,如果读取的字节较少,还会返回一个错误。若没有读取到字节,错误就只是 EOF。如果一个 EOF 发生在读取了一些但不是所有的字节后,ReadFull 就会返回 ErrUnexpectedEOF。对于返回值,当且仅当 err == nil 时,才有 n == len(buf)。

1
func ReadFull(r Reader, buf []byte)(n int, err error)

17. WriteString函数

  WriteString 将s的内容写入w中,当 w 实现了 WriteString 方法时,会直接调用该方法,否则执行 w.Write([]byte(s))。

1
func WriteString(w writer, s string)(n int, err error)

18. MultiReader和MultiWriter函数

1
2
3
4
5
6
7
8
9
10
type multiReader struct {
readers []Reader
}

type multiWriter struct {
writers []Writer
}

func MultiReader(readers ...Reader) Reader
func MultiWriter(writers ...Writer) Writer

使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
readers := []io.Reader{
strings.NewReader("from strings reader"),
bytes.NewBufferString("from bytes buffer"),
}
reader := io.MultiReader(readers...)
data := make([]byte, 0, 128)
buf := make([]byte, 10)

for n, err := reader.Read(buf); err != io.EOF ; n, err = reader.Read(buf){
if err != nil{
panic(err)
}
data = append(data,buf[:n]...)
}
fmt.Printf("%s\n", data)
//输出
//from strings readerfrom bytes buffer
1
2
3
4
5
6
7
8
9
10
11
12
13
file, err := os.Create("tmp.txt")
if err != nil {
panic(err)
}
defer file.Close()
writers := []io.Writer{
file,
os.Stdout,
}
writer := io.MultiWriter(writers...)
writer.Write([]byte("Hello World!"))

//输出:控制台和文件同时输出Hello World!

参考文章:
《Go语言标准库》

  • 本文标题:Golang基本输入输出(io包接口)
  • 本文作者:lookupman
  • 创建时间:2020-10-25 16:22:58
  • 本文链接:https://lookupman.cn/2020/10/25/Golang基本输入输出(io包接口)/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!