Go_io.Reader

Go_io.Reader


io.Reader

io的Reader是一个接口,实现了Read的方法都是一个读取器
一般读取有三种情况

  • Read方法读取p的长度的内容到p中,并返回读取的长度n,n <= len§
  • 当实现reader的对象被读取完了,n=0,err不会等于nil,而是返回EOF
  • p的长度为0,返回0,nil
type Reader interface {
    Read(p []byte) (n int, err error)
}

实现Reader接口的结构【主要前4个,后面4个就是提一下有实现这个接口】


strings.Reader
type Reader struct {
	s        string
	i        int64 // 当前读取的下标
	prevRune int   
}

strings.Reader实现的Read方法,如果当前读取下标大于等于s的长度,表示已经读完了。反之,把i下标之后的内容拷贝给b,拷贝的长度为n。当前读取下标更新,加上n的长度。

func (r *Reader) Read(b []byte) (n int, err error) {
	if r.i >= int64(len(r.s)) {
		return 0, io.EOF
	}
	r.prevRune = -1
	n = copy(b, r.s[r.i:])
	r.i += int64(n)
	return
}

例子

s := strings.NewReader("abcdefghij")

// a  b  c  d  e  f  g  h  i  j
// 1  2  3  4  5  6  7  8  9  10

// Len作用: 返回未读的字符串长度
// Size的作用:返回字符串的长度

fmt.Printf("len : %d\n", s.Len()) //还未被读的string的长度 ===> 10

fmt.Printf("size : %d\n", s.Size()) // 原始string长度,不会被改变的,每次调用都是一样的 ===> 10

// Read会影响未读长度的数值,为r.i+n,n为读的长度
buf1 := make([]byte, 5)
_, _ = s.Read(buf1)

fmt.Println("buffer read : ", string(buf1)) // ===> abcde
fmt.Printf("len : %d\n", s.Len()) //还未被读的string的长度 ===> 5
fmt.Printf("size : %d\n", s.Size()) // 原始string长度,不会被改变的,每次调用都是一样的 ===> 10
bytes.Reader
type Reader struct {
    s        []byte   
    i        int64 // 当前读取的下标
    prevRune int   //
}

和strings.Reader是一样的,不同的是r.s是[]byte

func (r *Reader) Read(b []byte) (n int, err error) {
	if r.i >= int64(len(r.s)) {
		return 0, io.EOF
	}
	r.prevRune = -1
	n = copy(b, r.s[r.i:])
	r.i += int64(n)
	return
}
bytes.Buffer
type Buffer struct {
	buf      []byte 
	off      int    // 读取或写入时的下标
	lastRead readOp // 记录上一次读的操作
}

bytes.Buffer读取的时候会记录下本次读取操作的结果,记在lastRead。

b.empty(),如果未读的部分小于r.buf的长度,表示已经读完了,那如果p的长度为0,表示就没有内容给它写入,err为nil。如果p有长度,b是空的,那需要回传EOF,已经读完了。

11~12行和strings.Reader是一样的记录位移。

13~15行如果读取内容的长度n > 0,表示这次读操作是有内容的,lastRead就要记为opRead。

bytes.Buffer和strings.Reader, bytes.Reader区别就在于需要记录读操作的结果,在Unread的时候,用来判断。

func (b *Buffer) Read(p []byte) (n int, err error) {
	b.lastRead = opInvalid // 重置lastRead的操作为non-read operation
	if b.empty() {  
		// Buffer is empty, reset to recover space.
		b.Reset()
		if len(p) == 0 {
			return 0, nil
		}
		return 0, io.EOF
	}
	n = copy(p, b.buf[b.off:])
	b.off += n
	if n > 0 {
		b.lastRead = opRead
	}
	return n, nil
}

例子

b := "abcdefghij"

buf := bytes.NewBuffer([]byte(b))

// a  b  c  d  e  f  g  h  i  j
// 1  2  3  4  5  6  7  8  9  10

// Len作用: 返回未读的字符串长度
// Cap的作用: 返回byte切片的容量

fmt.Printf("len : %d\n", buf.Len()) // 还未被读的buf的字节长度 ===> 10
fmt.Printf("cap : %d\n", buf.Cap()) // byte切片的容量,不会变动 ===> 32

// Read会影响未读长度的数值,为r.i+n,n为读的长度
tmp := make([]byte, 5)
_, _ = buf.Read(tmp)

fmt.Println("buffer read : ", string(tmp)) // ===> abcde
fmt.Printf("len : %d\n", buf.Len())   // 还未被读的buf的字节长度 ===> 5
fmt.Printf("cap : %d\n", buf.Cap())  // byte切片的容量,不会变动 ===> 32
bufio.Reader
type Reader struct {
	buf          []byte
	rd           io.Reader // reader provided by the client
	r, w         int       // buf read and write positions
	err          error
	lastByte     int // last byte read for UnreadByte; -1 means invalid
	lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}

第9行判断buf是否有缓存字节,因为如果有缓存字节,b.r和b.w是不相等的。因此,如果有缓存字节,会先从buf读取缓存,即第41行,把缓存的字节都读出来。

没有缓存则要判断,缓存长度(bl: buffer length)和要读取的长度(rl: read length),即p的长度。如果rl >= bl,第13行,直接从b.rd读取,不启用缓存。反之,先读到缓存里面,再从缓存读给p。

条件执行
1. 有缓存从缓存内直接读取,最多读取bl长度的字节,即使rl > bl
2. 无缓存 rl >= bl直接从rd io.Reader里面读取rl长度的字节,不用缓存
3. 无缓存 rl <= bl先读取bl长度的字节到缓存内,再读取缓存内rl长度的字节
func (b *Reader) Read(p []byte) (n int, err error) {
	n = len(p)
	if n == 0 {
		if b.Buffered() > 0 {
			return 0, nil
		}
		return 0, b.readErr()
	}
	if b.r == b.w {
		if b.err != nil {
			return 0, b.readErr()
		}
		if len(p) >= len(b.buf) {
			// Large read, empty buffer.
			// Read directly into p to avoid copy.
			n, b.err = b.rd.Read(p)
			if n < 0 {
				panic(errNegativeRead)
			}
			if n > 0 {
				b.lastByte = int(p[n-1])
				b.lastRuneSize = -1
			}
			return n, b.readErr()
		}
		// One read.
		// Do not use b.fill, which will loop.
		b.r = 0
		b.w = 0
		n, b.err = b.rd.Read(b.buf)
		if n < 0 {
			panic(errNegativeRead)
		}
		if n == 0 {
			return 0, b.readErr()
		}
		b.w += n
	}

	// copy as much as we can
	n = copy(p, b.buf[b.r:b.w])
	b.r += n
	b.lastByte = int(b.buf[b.r-1])
	b.lastRuneSize = -1
	return n, nil
}

例子

// 一个IO对象
	stringReader := strings.NewReader("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

	// go 的缓冲区最小为 16 byte
	bufReader := bufio.NewReaderSize(stringReader, 16)

	// 1.  => 6 abcdefghij  条件3
	tmpBytSlice := make([]byte, 10)
	n, _ := bufReader.Read(tmpBytSlice)
	fmt.Printf("buffer unread: %d, tmpBytSlice: %s\n", bufReader.Buffered(), tmpBytSlice[:n])


	// 2.  => 0 klmnop  条件1
	tmpBytSlice = make([]byte, 19)
	n, _ = bufReader.Read(tmpBytSlice)
	// bufReader buffered: 1, content: 789012345678901
	fmt.Printf("buffer unread: %d, tmpBytSlice: %s\n", bufReader.Buffered(), tmpBytSlice[:n])


	// 3.  => 0 qrstuvwxyzABCDEFGH  条件2
	tmpBytSlice = make([]byte, 18)
	n, _ = bufReader.Read(tmpBytSlice)
	fmt.Printf("buffer unread: %d, tmpBytSlice: %s\n", bufReader.Buffered(), tmpBytSlice[:n])


	// 4.  => 12 IJKL 条件3
	tmpBytSlice = make([]byte, 4)
	n, _ = bufReader.Read(tmpBytSlice)
	// bufReader buffered: 6, content: 3456789012
	fmt.Printf("buffer unread: %d, tmpBytSlice: %s\n", bufReader.Buffered(), tmpBytSlice[:n])


	// 5.  => 7  MNO  条件3
	tmpBytSlice = make([]byte, 3)
	n, _ = bufReader.Read(tmpBytSlice)
	fmt.Printf("buffer unread: %d, tmpBytSlice: %s\n", bufReader.Buffered(), tmpBytSlice[:n])
os.File
type File struct {
	*file // os specific
}

type file struct {
	pfd        poll.FD  // 文件描述器,net包或package包会嵌套该类型,用于表示一个net的连接或者OS文件
	name       string
	dirinfo    *dirInfo // nil unless directory being read
	appendMode bool     // whether file is opened for appending
}

os.Open(filename)会返回一个*os.File的结构体,这个结构体实现了io.Reader。

File的Read,会先判断该文件是否是可读的。

然后通过f.pfd实现的Read把文件内容写到b里。

file底层读取的方法是直接呼叫system call。

func (f *File) Read(b []byte) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	n, e := f.read(b)
	return n, f.wrapErr("read", e)
}

func (f *File) read(b []byte) (n int, err error) {
	n, err = f.pfd.Read(b)
	runtime.KeepAlive(f)
	return n, err
}

func (fd *FD) Read(buf []byte) (int, error) {
	if err := fd.readLock(); err != nil {  //读锁
		return 0, err
	}
	defer fd.readUnlock()

	if len(buf) > maxRW {  //最大读取长度为1G
		buf = buf[:maxRW] 
	}

	var n int
	var err error
	if fd.isFile {  //判断是否为文件
		fd.l.Lock()  //锁文件
		defer fd.l.Unlock()
		switch fd.kind {
		case kindConsole:
			n, err = fd.readConsole(buf)
		default:
			n, err = syscall.Read(fd.Sysfd, buf)  // system call 读取文件
			if fd.kind == kindPipe && err == syscall.ERROR_OPERATION_ABORTED {
				// Close uses CancelIoEx to interrupt concurrent I/O for pipes.
				// If the fd is a pipe and the Read was interrupted by CancelIoEx,
				// we assume it is interrupted by Close.
				err = ErrFileClosing
			}
		}
		if err != nil {
			n = 0
		}
	} else {
		...
	}
	if len(buf) != 0 {  //如果读取完之后,没有未读的内容就要返回EOF
		err = fd.eofError(n, err)
	}
	return n, err
}
net.Conn

net.Conn的netFD也是组合了poll.FD和os.File是一样的,底层调用的read是一样的,取决于fd.Sysfd初始化的时候调用到的system call,而生成的handler决定。

type conn struct {
	fd *netFD
}

type netFD struct {
	pfd poll.FD

	// immutable until Close
	family      int
	sotype      int
	isConnected bool // handshake completed or use of association with peer
	net         string
	laddr       Addr
	raddr       Addr
}
os.Stdin, os.Stdout, os.Stderr

这三个都是通过读取文件,指向standard input, standard output, standard error,os.File本身是实现io.Reader,所以这三个也实现了io.Reader

var (
    Stdin  = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
	Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
	Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
ioutil

ioutil包里面的ReadAll,ReadFile,ReadDir就是封装实现io.Reader的一些结构

资料来源

  1. https://segmentfault.com/a/1190000019489118
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值