io的Reader是一个接口,实现了Read的方法都是一个读取器
一般读取有三种情况
type Reader interface {
Read(p []byte) (n int, err error)
}
实现Reader接口的结构【主要前4个,后面4个就是提一下有实现这个接口】
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
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
}
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
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])
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的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
}
这三个都是通过读取文件,指向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包里面的ReadAll,ReadFile,ReadDir就是封装实现io.Reader的一些结构
资料来源