Go语言IO操作 - 1 interface io.Reader

interface io.Reader定义

1
2
3
4
5
// go/src/io/io.go

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

interface io.Reader语义

Reader中读取数据,存入传入参数p中,并通过返回值n返回本次读取的大小,以及err返回可能存在的错误。

对于一次调用:

可能发生阻塞;不保证读够(读满)len(p)的数据

  • n取值范围0 <= n <= len(p)
  • 即使读取到的数据n < len(p),也不保证p内存块后半部分的原始数据保持不变,不被修改
  • 只要有数据可读,即使可读数据n < len(p),也会立即返回,而不是等待读够len(p)大小的数据

注意,如果没有数据可读并且也没有错误发生,会阻塞当前协程,直到数据可读或发生错误。
(注意,这里说的阻塞指的是上层代码的表现,Go的IO线程协程调度模型不在本文讨论范围内)

正确处理返回值

可能同时出现n > 0并且err != nil的情况。

当读取到结尾EOF(end-of-file)时,可以有两种方式

  1. 首次调用返回n > 0并且err != nil
  2. 首次调用返回n > 0并且err == nil

第二次调用返回n == 0并且err == EOF

第一种方式,即使err != nil,也有可能读取到了数据。

所以,不管是以上哪种方式,正确处理应该是每次调用结束后,先判断n,处理读取到的数据,再判断err,像这样:

1
2
3
4
5
6
7
8
n, err := r.Read(p)
if n > 0 {
// ...
// 注意,此处并没有return等跳过下面判断err值的逻辑
}
if err != nil {
// ...
}
n == 0并且err == nil
  • 正常来说,不应该出现n == 0的情况,除非len(p) == 0
  • n == 0并且err == nil时,应被认为啥事也没发生(一次空调用),而不是EOF

其他

函数调用结束后,内部不会持有p的内存

interface io.Reader举例:

1
2
3
4
5
6
7
8
9
10
11
12
// 1. 读取文件
// go/src/os/file.go
func (f *File) Read(b []byte) (n int, err error) {
// ...
}

// 2. 读取网络连接
// go/src/net/net.go
type Conn interface {
Read(p []byte) (n int, err error)
// ...
}

本文完,作者yoko,尊重劳动人民成果,转载请注明原文出处: https://pengrl.com/p/20120/

0%