关于io包

io包提供了对I/O原语的基本接口。本包的基本任务是包装这些原语已有的实现(如os包里的原语),使之成为共享的公共接口,这些公共接口抽象出了泛用的函数并附加了一些相关的原语的操作。

因为这些接口和原语是对底层实现完全不同的低水平操作的包装,除非得到其它方面的通知,客户端不应假设它们是并发执行安全的。

原语:计算机进程的控制通常由原语完成。所谓原语,一般是指由若干条指令组成的程序段,用来实现某个特定功能,在执行过程中不可被中

错误变量

// ErrShortWrite意味着写入接受的字节数少于请求的字节数,但未能返回显式错误
var ErrShortWrite = errors.New("short write")

// 表示读取所需的缓冲区比提供的缓冲区长。
var ErrShortBuffer = errors.New("short buffer")

// EOF是当没有更多的输入可用时,Read返回的错误
// 函数应该只返回EOF来表示输入的优美结束,如果EOF在结构化数据流中意外发生,则相应的错误可能是ErrUnexpectedEOF ,也可能是提供更多细节的其他错误。
var EOF = errors.New("EOF")

// 表示在读取固定大小的块或数据结构时遇到EOF。
var ErrUnexpectedEOF = errors.New("unexpected EOF")

// 当许多Read 读取调用未能返回任何数据或错误(通常是io.Reader实现中断的标志)时,ioReader的某些客户端将返回ErrNoProgress
var ErrNoProgress = errors.New("multiple Read calls return no data or error")

基础接口

1、Reader 接口
只要实现了 Read()方法的对象都可以作为io.Read接口传递
Read() 方法 将len(p)字节读入p, 返回读取的字节数(0<=n<=len§)。即使Read返回n<len(p),它也可能在调用期间使用所有p作为暂存空间。 若一些数据可用但不到 len§ 个字节,Read 会照例返回可用的东西,而不是等待更多。
当Read在成功读取n>0字节后遇到错误或文件结束条件时,它返回读取的字节数。它会从相同的调用中返回(非nil的)错误或从随后的调用中返回错误(和 n == 0)

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

2、Writer接口
只要实现了 Write()方法的对象都可以作为io.Writer接口传递
Write() 方法len( p )字节数据从p写入底层的数据流。它会返回写入的字节数(0 <= n <= len( p ))和遇到的任何导致写入提取结束的错误。
Write必须返回非nil的错误,如果它返回的 n < len( p )。Write不能修改切片p中的数据,即使临时修改也不行。

type Writerinterface {
	Write(p []byte) (n int, err error)	
	}

3、Closer接口
Closer关闭的接口, 带有Close() 方法, 但是行为没有定义,所以 可以特定行为来实现
在整个标准库内都没有对Closer的引用,只有实现,用法都是开启某某连接/流,在用完/报错后在进行Close的操作。

type Closer interface {
	Close() error
}

4、Seeker接口
Seeker接口用于包装基本的移位方法。返回相对于文件开头的新偏移量(新的位置)和错误

Seek()设置设定下一次读写的位置:偏移量为offset,校准点由whence确定:0 SeekStart表示相对于文件起始;1 SeekCurrent表示相对于当前位置;2 SeekEnd表示相对于文件结尾。

移动到一个绝对偏移量为负数的位置会导致错误。移动到任何偏移量为正数的位置都是合法的,但其下一次I/O操作的具体行为则要看底层的实现。

在整个标准库内都没有对Seeker的引用,只有实现

type Seeker interface {
	Seek(offset int64, whence int) (int64, error)
}

组合接口

组合接口是对多个接口进行了组合,当同时实现多个接口时,可以使用组合接口进行传递\

// 对基本读写方法进行分组的接口
type ReadWriter interface {
	Reader
	Writer
}
// 带有 Closer 关闭 接口的 Reader 新接口
// 是对基本的Read 和Close方法 进行分组的接口
type ReadCloser interface {
	Reader
	Closer
}
// 是对基本写入和关闭方法进行分组的接口
type WriteCloser interface {
	Writer
	Closer
}
// 是对基本的读、写和关闭方法进行分组的接口。
type ReadWriteCloser interface {
	Reader
	Writer
	Closer
}
// 对基本的Read和Seek方法进行分组的接口。
type ReadSeeker interface {
	Reader
	Seeker
}
// 是对基本的写入和查找方法进行分组的接口。
type WriteSeeker interface {
	Writer
	Seeker
}
// 是一个接口,它将基本的读、写和查找方法分组。
type ReadWriteSeeker interface {
	Reader
	Writer
	Seeker
}

指定读写器 读写接口

1、ReaderFrom接口
ReadFrom方法的包装,该方法 从r读取数据,直到EOF或出错。n是读取的字节数。 Copy 函数使用ReaderFrom(如果可用)

type ReaderFrom interface {
	ReadFrom(r Reader) (n int64, err error)		
}

2、WriterTo接口
WriterTo方法的包装, 将数据写入w,直到没有更多数据可写入或出现错误,n是写入的字节数

type WriterTo interface {
	WriteTo(w Writer) (n int64, err error)		
}

指定偏移量 读写接口

1、ReaderAt接口
ReadAt从底层输入流的偏移量off位置读取len§字节数据写入p, 它返回读取的字节数(0 <= n <= len§)和遇到的任何错误。当ReadAt方法返回值n < len§时,它会返回一个非nil的错误来说明为啥没有读取更多的字节。在这方面,ReadAt是比Read要严格的。即使ReadAt方法返回值 n < len§,它在被调用时仍可能使用p的全部长度作为暂存空间。如果有部分可用数据,但不够len§字节,ReadAt会阻塞直到获取len§个字节数据或者遇到错误。在这方面,ReadAt和Read是不同的。如果ReadAt返回时到达输入流的结尾,而返回值n == len§,其返回值err既可以是EOF也可以是nil。

如果ReadAt是从某个有偏移量的底层输入流(的Reader包装)读取,ReadAt方法既不应影响底层的偏移量,也不应被底层的偏移量影响。

ReadAt方法的调用者可以对同一输入流执行并行的ReadAt调用。

type ReaderAt interface {
	ReadAt(p []byte, off int64) (n int, err error)
	}

2、WriterAt接口
WriterAt接口包装了基本的WriteAt方法。

WriteAt将p全部len§字节数据写入底层数据流的偏移量off位置。它返回写入的字节数(0 <= n <= len§)和遇到的任何导致写入提前中止的错误。当其返回值n < len§时,WriteAt必须放哪会一个非nil的错误。

如果WriteAt写入的对象是某个有偏移量的底层输出流(的Writer包装),WriteAt方法既不应影响底层的偏移量,也不应被底层的偏移量影响。

ReadAt方法的调用者可以对同一输入流执行并行的WriteAt调用。(前提是写入范围不重叠)

type WriterAt interface {
	WriteAt(p []byte, off int64) (n int, err error)
	}

单个字节读写接口

9、ByteReader接口
ByteReader是基本的ReadByte方法的包装。
ReadByte读取输入中的单个字节并返回。如果没有字节可读取,会返回错误。

不实现ByteReader的读取器可以使用bufio.NewReader添加此方法:
调用func NewReader(rd io.Reader) Reader返回Reader类型参数,
调用func (b *Reader) ReadByte() (c byte, err error)返回单个字节

type ByteReader interface {
	ReadByte() (byte, error)
	}

10、ByteScanner接口
ByteReader接口的包装,新增了UnreadByte方法,该方法导致下一次对ReadByte方法的调用返回与上一次对ReadByte的调用相同的字节。两次调用UnreadByte而不调用ReadByte可能会导致错误

type ByteScanner interface {
	ByteReader
	UnreadByte() error
}

11、ByteWriter接口
包装 WriteByte 单个字节写入方法的接口

type ByteWriter interface {
	WriteByte(c byte) error
}

12、RuneReader接口
ReadRune 方法的包装,读取单个UTF-8编码的Unicode字符,并返回rune及其字节大小。如果没有可用字符,将设置err

type RuneReader interface {
	ReadRune() (r rune, size int, err error)	
}

13、RuneScanner接口
RuneReader接口的包装,新增了UnreadRune方法
UnreadRune方法让下一次调用ReadRune时返回之前调用ReadRune时返回的同一个utf-8字符。连续调用两次UnreadRune方法而中间没有调用ReadRune时,可能会导致错误。

type RuneScanner interface {
	RuneReader
	UnreadRune() error
}

14、StringWriter接口
字符串写入方法WriteString的包装

type StringWriter interface {
	WriteString(s string) (n int, err error)
}

可供外部调用的WriteString方法: 将字符串s的内容写入w,w接受字节片。如果w实现了StringWriter,则直接调用其WriteString方法,否则w.Write只调用一次

func WriteString(w Writer, s string) (n int, err error) {
	if sw, ok := w.(StringWriter); ok {
		return sw.WriteString(s)
	}
	return w.Write([]byte(s))
}

供外部调用的 单独方法

1、func ReadAtLeast
ReadAtLeast从r至少读取min字节数据填充进缓冲器buf。函数返回写入的字节数和错误(如果没有读取足够的字节)。只有没有读取到字节时才可能返回EOF;如果读取了有但不够的字节时遇到了EOF,函数会返回ErrUnexpectedEOF。 如果min比buf的长度还大,函数会返回ErrShortBuffer。只有返回值err为nil时,返回值n才会不小于min。

func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error) {
	// 1、 如果缓冲器 太小 则报错
	if len(buf) < min {
		return 0, ErrShortBuffer
	}
	// 2、循环读直到 n等于min或者出错
	for n < min && err == nil {
		var nn int
		// 3、先调用读取器的Read方法,读取 len(buf[n:])个字节进入 buf缓冲器,然后返回 读取的字节数nn
		nn, err = r.Read(buf[n:])
		// 4、然后 将读出的 字节累加,继续循环判断
		n += nn
	}
	// 3、如果结束循环后,至少读出了n字节,则将err赋值nil
	if n >= min {
		err = nil
		// 4、如果 读出了字节且出错,即读取少于min字节后发生EOF,则err返回errunexpectedof
	} else if n > 0 && err == EOF {
		err = ErrUnexpectedEOF
	}
	return
}

2、func ReadFull
ReadFull 从r读取器 精确地读取len(buf)字节数据填充进buf。函数返回写入的字节数和错误(如果没有读取足够的字节)。只有没有读取到字节时才可能返回EOF;如果读取了有但不够的字节时遇到了EOF,函数会返回ErrUnexpectedEOF。 只有返回值err为nil时,返回值n才会等于len(buf)。

func ReadFull(r Reader, buf []byte) (n int, err error) {
	return ReadAtLeast(r, buf, len(buf))
}

3、func CopyN
从src读取器 拷贝n个字节数据到dst,直到在src上到达EOF或发生错误。返回复制的字节数和遇到的第一个错误。
只有err为nil时,written才会等于n。如果dst实现了ReaderFrom接口,本函数很调用它实现拷贝。

func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {
	// 1、调用Copy方法实现
	written, err = Copy(dst, LimitReader(src, n))
	// 2、判断返回结果
	if written == n {
		return n, nil
	}
	// 3、当读取器 较早停止,则written < n,此时报错
	if written < n && err == nil {
		err = EOF
	}
	return
}

4、func Copy
内部方法 copyBuffer()的封装,即不指定缓冲器进行copy,系统会分配一个缓冲器
将src的数据拷贝到dst,直到在src上到达EOF或发生错误。返回拷贝的字节数和遇到的第一个错误。

对成功的调用,返回值err为nil而非EOF,因为Copy定义为从src读取直到EOF,它不会将读取到EOF视为应报告的错误。如果src实现了WriterTo接口,本函数会调用src.WriteTo(dst)进行拷贝;否则如果dst实现了ReaderFrom接口,本函数会调用dst.ReadFrom(src)进行拷贝。

func Copy(dst Writer, src Reader) (written int64, err error) {
	return copyBuffer(dst, src, nil)
}

5、func CopyBuffer
内部方法 copyBuffer()的封装,即指定缓冲器进行copy

func CopyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
	if buf != nil && len(buf) == 0 {
		panic("empty buffer in io.CopyBuffer")
	}
	return copyBuffer(dst, src, buf)
}

6、内部实现func copyBuffer

func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {
	// 1、如果读取器有一个 WriteTo 方法, 使用该方法进行copy,避免分配和复制
	if wt, ok := src.(WriterTo); ok {
		return wt.WriteTo(dst)
	}
	// 2、类似地,如果writer 写入器有ReadFrom方法,则使用它进行复制
	if rt, ok := dst.(ReaderFrom); ok {
		return rt.ReadFrom(src)
	}
	// 3、如果缓冲器为空,则分配一个
	if buf == nil {
		size := 32 * 1024
		// 4、如果读取器限制读,且 分配的缓冲器大小 大于 限制读的大小
		if l, ok := src.(*LimitedReader); ok && int64(size) > l.N {
			// 当限制读 小于1时,分配的缓冲器也 设置成1,以限制为准
			if l.N < 1 {
				size = 1
			} else {
				// 否则,就直接赋值成限制读的大小
				size = int(l.N)
			}
		}
		buf = make([]byte, size)
	}
	for {
		// 5、从读取器中读入 进 buf
		nr, er := src.Read(buf)
		if nr > 0 {
			// 6、从buf 中写入底层数据流
			nw, ew := dst.Write(buf[0:nr])
			if nw > 0 {
				// 7、写入后累加
				written += int64(nw)
			}
			if ew != nil {
				err = ew
				break
			}
			// 8、当写入底层数据流 的字节数,不等于 读取的字节数,则 报错 :写入不够
			if nr != nw {
				err = ErrShortWrite
				break
			}
		}
		if er != nil {
			if er != EOF {
				err = er
			}
			break
		}
	}
	return written, err
}

结构体

1、Type LimitedReader
LimitedReader 限制读取器 从R中读取数据,但限制可以读取的数据的量为最多N字节,每次调用Read方法都会更新N以标记剩余可以读取的字节数。

type LimitedReader struct {
    R   Reader // 底层Reader接口
    N   int64  // 剩余可读取字节数
}

该结构体的 读取方法,实现了Reader接口

func (l *LimitedReader) Read(p []byte) (n int, err error) {
	// 1、如果限制字节数小于0则返回	EOF
	if l.N <= 0 {
		return 0, EOF
	}
	// 2、如果 读取的缓冲器大小 大于 限制字节数,则将p截断
	if int64(len(p)) > l.N {
		p = p[0:l.N]
	}
	// 3、开始读取 n个字节进入 p
	n, err = l.R.Read(p)
	// 4、剩余字节数减少n
	l.N -= int64(n)
	return
}

该结构体作为 Read接口返回的LimitReader方法:使用该结构体实例,返回一个Reader

func LimitReader(r Reader, n int64) Reader { return &LimitedReader{r, n} }

2、type SectionReader
SectionReader 分区读取器 在底层ReaderAt的某个节上实现Read、Seek和ReadAt

type SectionReader struct {
	// 非导出字段
	r     ReaderAt
	base  int64
	off   int64
	limit int64
}

该结构体的 读取方法,实现了Reader接口

func (s *SectionReader) Read(p []byte) (n int, err error) {
	// 1、如果 偏移量 大于 限制量,则 出错
	if s.off >= s.limit {
		return 0, EOF
	}
	// 2、max= 限制量-偏移量,如果缓冲器 大小 大于max,则裁断 缓冲器
	if max := s.limit - s.off; int64(len(p)) > max {
		p = p[0:max]
	}
	// 3、使用偏移量读取 方法进行读取
	n, err = s.r.ReadAt(p, s.off)
	s.off += int64(n)
	return
}

结构体实例 创建

func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader {
	return &SectionReader{r, off, off, off + n}
}

结构体实现Seek接口
根据 传入的校准点whencewhence,设置偏移量

func (s *SectionReader) Seek(offset int64, whencewhence int) (int64, error) {
	switch whence {
	default:
		return 0, errWhence
	case SeekStart:
		offset += s.base
	case SeekCurrent:
		offset += s.off
	case SeekEnd:
		offset += s.limit
	}
	if offset < s.base {
		return 0, errOffset
	}
	s.off = offset
	return offset - s.base, nil
}

结构体实现ReadAt接口

func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
	// 1、入参偏移量判断
	if off < 0 || off >= s.limit-s.base {
		return 0, EOF
	}
	// 2、默认从起始点 为 校准点
	off += s.base
	// 3、如果 缓冲大小 大于 读取器 读入的大小,则裁剪 缓冲器
	if max := s.limit - off; int64(len(p)) > max {
		p = p[0:max]
		// 4、调用定点 读取方法 开始读取
		n, err = s.r.ReadAt(p, off)
		if err == nil {
			err = EOF
		}
		return n, err
	}
	// 4、否则直接 使用缓冲器 进行读取
	return s.r.ReadAt(p, off)
}

3、teeReader 内部结构体
返回一个读卡器,该读卡器将从r读取的内容写入w。通过它从r执行的所有读操作都与对w的相应写操作相匹配,没有内部缓冲-写入必须在读取完成之前完成

type teeReader struct {
	r Reader
	w Writer
}

返回结构体实例

func TeeReader(r Reader, w Writer) Reader {
	return &teeReader{r, w}
}

结构体实现Reader接口

func (t *teeReader) Read(p []byte) (n int, err error) {
	// 1、先进行读取
	n, err = t.r.Read(p)
	if n > 0 {
		// 2、然后进行 写入
		if n, err := t.w.Write(p[:n]); err != nil {
			return n, err
		}
	}
	return
}