博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
golang io中io.go解读
阅读量:5062 次
发布时间:2019-06-12

本文共 12904 字,大约阅读时间需要 43 分钟。

目录

根据golang io源码包解读io.go文件。

1. 整体大纲

1481607-20190802143641778-1270008145.png

分别从接口,函数以及结构体去解读golang io 包中io.go文件。


2. 接口

在源代码中,对于 IO 流,定义了四个基本操作原语,分别用 Reader,Writer,Closer,Seeker 接口表达二进制流读、写、关闭、寻址等操作。根据其中的性质来区分,将分为读,写,关闭以及寻址等解读。

详细实现参考: bytes.Buffer

Reader

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

Reader 接口包装了基本的 Read 方法,用于输出自身(实现者)的数据到p。Read 方法用于将对象的数据流读入到 p 中,返回读取的字节数和遇到的错误。实现者不能包含p。

  • 在没有遇到读取错误的情况下:
    • 如果读到了数据(n > 0),则应该返回 n,nil。
    • 如果数据被读空,没有数据可读(n == 0),则应该返回 0,EOF;
  • 遇到读取错误,则 err 应该返回相应的错误信息(如果在读取过程中发了错误即n>0,那么要考虑处理这种情况,返回错误为ErrUnexpectedEOF);
  • 返回0,nil,那么代表什么都没有发生。
buf := bytes.NewBuffer([]byte("Hello World!"))    b := make([]byte, buf.Len())    n, err := buf.Read(b)    fmt.Printf("%s   %v\n", b[:n], err) // output: Hello World!   
ReaderFrom
type ReaderFrom interface {    ReadFrom(r Reader) (n int64, err error)}

ReaderFrom 接口包装了基本的 ReadFrom 方法,用于从 r 中读取数据存入自身(即实现者本身带有p)。 直到遇到 EOF 或读取出错为止,返回读取的字节数和遇到的错误。

buf := bytes.NewBuffer([]byte("Hello World!"))    dst := bytes.Buffer{}    dst.ReadFrom(buf)    dst.WriteTo(os.Stdout) // output: Hello World!

ReaderAt

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

ReaderAt 接口包装了基本的 ReadAt 方法,用于将自身的数据写入 p 中。ReadAt 忽略之前的读写位置,从起始位置的 off 偏移处开始读取。

返回写入的字节数和遇到的错误:

  • 如果 p 被写满,则 err 会返回 nil;
  • 如果 p 没 有被写满,则会返回一个错误信息用于说明为什么没有写满(比如 io.EOF)。在这方面 ReadAt 比 Read 更严格。
  • 如果 p 被写满的同时,自身的数据也刚好被读完,则 err 即可以返回 nil 也可以返回 io.EOF。

即使不能将 p 填满,ReadAt 在被调用时也可能会使用整个 p 的空间作为缓存空间。如果 ReadAt 自身的数据是从其它地方(比如网络)获取数的,那么在写入 p 的时候,如果没有把 p 写满(比如网络延时),则 ReadAt 会阻塞,直到获取更多的数据把 p 写满,或者所有数据都获取完毕,或者遇到读取错误(比如超时)时才返回。

在这方面,ReadAt 和 Read 是不同的。

如果 ReadAt 读取的对象是某个有偏移量的底层数据流时,则 ReadAt 方法既不能影响底层的偏移量,也不应该被底层的偏移量影响。

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

ReaderAt 的实现者不应该持有 p。

ByteReader

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

ByteReader 接口包装了基本的 ReadByte 方法,用于从自身读出一个字节。

返回读出的字节和遇到的错误。如果返回错误,那么没有任何输入byte被消费,所返回的byte也是无效的。

buf := bytes.NewBuffer([]byte("Hello World!"))    c, err := buf.ReadByte()    fmt.Printf("%c   %s   %v\n", c, buf.String(), err) // output: H   ello World!   
ByteScanner
type ByteScanner interface {    ByteReader    UnreadByte() error}

ByteScanner 在 ByteReader 的基础上增加了一个 UnreadByte 方法,用于撤消最后一次的 ReadByte 操作,以便下次的 ReadByte 操作可以读出与前一次一样的数据。

UnreadByte 之前必须是 ReadByte 才能撤消成功,否则可能会返回一个错误信息(根 据不同的需求,UnreadByte 也可能返回 nil,允许随意调用 UnreadByte,但只有最后一次的 ReadByte 可以被撤销,其它 UnreadByte 不执行任何操作)。

buf := bytes.NewBuffer([]byte("Hello World!"))    c, err := buf.ReadByte()    fmt.Printf("%c   %s   %v\n", c, buf.String(), err)// output: H   ello World!   
err = buf.UnreadByte() fmt.Printf("%s %v\n", buf.String(), err)//output: Hello World!

RuneReader

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

RuneReader 接口包装了基本的 ReadRune 方法,用于从自身读取一个 UTF-8 编码的字符到 r 中。

返回读取的字符、字符的编码长度和遇到的错误。

buf := bytes.NewBuffer([]byte("爱Hello World!"))    c,s, err := buf.ReadRune()    fmt.Printf("%c  %d  %s   %v\n", c,s, buf.String(), err) // output: 爱  3  Hello World!   
RuneScanner
type RuneScanner interface {    RuneReader    UnreadRune() error}

RuneScanner 在 RuneReader 的基础上增加了一个 UnreadRune 方法,用于撤消最后一次的 ReadRune 操作,以便下次的 ReadRune 操作可以读出与前一次一样的数据。UnreadRune(操作) 之前必须是 ReadRune(操作) 才能撤消成功,否则可能会返回一个错误信息(根据不同的需求,UnreadRune 也可能返回 nil,允许随意调用 UnreadRune,但只有最后一次的 ReadRune 可以被撤销,其它 UnreadRune 不执行任何操作)。

buf := bytes.NewBuffer([]byte("爱Hello World!"))    c,s, err := buf.ReadRune()    fmt.Printf("%c  %d  %s   %v\n", c,s, buf.String(), err) // output: 爱  3  Hello World!   
err = buf.UnreadRune() fmt.Printf("%c %d %s %v\n", c,s, buf.String(), err)// output: 爱 3 爱Hello World!

Writer

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

Writer 接口包装了基本的 Write 方法,用于将数据存入自身。Write 方法用于将 p 中的数据写入到对象的数据流中,返回写入的字节数和遇到的错误。

  • 如果 p 中的数据全部被写入,则 err 应该返回 nil。
  • 如果 p 中的数据无法被全部写入,则 err 应该返回相应的错误信息。
WriterTo
type WriterTo interface {    WriteTo(w Writer) (n int64, err error)}

WriterTo 接口包装了基本的 WriteTo 方法,用于将自身的数据写入 w 中。

直到数据全部写入完毕或遇到错误为止,返回写入的字节数和遇到的错误。

WriterAt

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

WriterAt 接口包装了基本的 WriteAt 方法,用于将 p 中的数据写入自身。

ReadAt 忽略之前的读写位置,从起始位置的 off 偏移处开始写入。

返回写入的字节数和遇到的错误。如果 p 没有被读完,则必须返回一个 err 值来说明为什么没有读完。

如果 WriterAt 写入的对象是某个有偏移量的底层数据流时,则 ReadAt 方法既不能影响底层的偏移量,也不应该被底层的偏移量影响。

WriterAt 的调用者可以对同一数据流的不同区段并行执行 WriteAt 方法。WriterAt 的实现者不应该持有 p。

ByteWriter

type ByteWriter interface {    WriteByte(c byte) error}

ByteWriter 接口包装了基本的 WriteByte 方法,用于将一个字节写入自身。

返回遇到的错误

关闭

Closer

type Closer interface {    Close() error}

Closer 接口包装了基本的 Close 方法,用于关闭数据读写。

Close 一般用于关闭文件,关闭通道,关闭连接,关闭数据库等

寻址

Seeker

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

Seeker 接口包装了基本的 Seek 方法,用于移动数据的读写指针。

Seek 设置下一次读写操作的指针位置,每次的读写操作都是从指针位置开始的。

whence 的含义:

  • 如果 whence 为 0:表示从数据的开头开始移动指针。
  • 如果 whence 为 1:表示从数据的当前指针位置开始移动指针。
  • 如果 whence 为 2:表示从数据的尾部开始移动指针。

offset 是指针移动的偏移量。返回新指针位置和遇到的错误。

r := strings.NewReader("Hello World!")    n, err := io.CopyN(os.Stdout, r, 5) // output: Hello    fmt.Printf("\n%d   %v\n\n", n, err) // output: 5   
r.Seek(0, 0) n, err = io.Copy(os.Stdout, r) // output: Hello World! fmt.Printf("\n%d %v\n\n", n, err) // output: 12

3. 函数

ReadFull

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

这个函数可以把对象 r 中的数据读出来,然后存入一个缓冲区 buf 中,以便其它代码可以处理 buf 中的数据。

如果没有数据读取,那么久返回拷贝的字节数和一个错误。

  • 返回n,EOF代表没有字节可以读取了
  • 返回ErrUnexpectedEOF,如果在读取数据的过程中发生了err
  • 返回 n == len(buf) 或者 err == nil,代表err不存在
// 定义一个 Ustr 类型type Ustr struct {    s string // 数据流    i int    // 读写位置}// 根据字符串创建 Ustr 对象func NewUstr(s string) *Ustr {    return &Ustr{s, 0}}// 获取未读取部分的数据长度func (s *Ustr) Len() int {    return len(s.s) - s.i}// 实现 Ustr 类型的 Read 方法func (s *Ustr) Read(p []byte) (n int, err error) {    for ; s.i < len(s.s) && n < len(p); s.i++ {        c := s.s[s.i]        // 将小写字母转换为大写字母,然后写入 p 中        if 'a' <= c && c <= 'z' {            p[n] = c + 'A' - 'a'        } else {            p[n] = c        }        n++    }    // 根据读取的字节数设置返回值    if n == 0 {        return n, io.EOF    }    return n, nil}func main() {    s := NewUstr("Hello World!") // 创建 Ustr 对象 s    buf := make([]byte, s.Len()) // 创建缓冲区 buf    n, err := io.ReadFull(s, buf) // 将 s 中的数据读取到 buf 中    fmt.Printf("%s\n", buf) //output:  HELLO WORLD!    fmt.Println(n, err)     //output:  12 
}

ReadAtLeast

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

ReadAtLeast 从 r 中读取数据到 buf 中,要求至少读取 min 个字节。

返回读取的字节数和遇到的错误。
如果 min 超出了 buf 的容量,则 err 返回 io.ErrShortBuffer,否则:

  • 读出的数据长度 == 0 ,则 err 返回 EOF;
  • 读出的数据长度 < min,则 err 返回 io.ErrUnexpectedEOF;
  • 读出的数据长度 >= min,则 err 返回 nil。
r := strings.NewReader("Hello World!") // 数据长度为12    b := make([]byte, 15)    n, err := io.ReadAtLeast(r, b, 12) // 要求读取至少12个字节    fmt.Printf("%q   %d   %v\n", b[:n], n, err) // output: "Hello World!"   12   

LimitReader

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

LimitReader 对 r 进行封装,使其最多只能读取 n 个字节的数据。相当于对 r 做了一个切片 r[:n] 返回。底层实现是一个 *LimitedReader(只有一个 Read 方法)。

r := strings.NewReader("Hello World!")    lr := io.LimitReader(r, 5)    n, err := io.Copy(os.Stdout, lr)  // Hello    fmt.Printf("\n%d   %v\n", n, err)  output: 5   

MultiReader

func MultiReader(readers ...Reader) Reader {    r := make([]Reader, len(readers))    copy(r, readers)    return &multiReader{r}}

MultiReader 将多个 Reader 封装成一个单独的 Reader,多个 Reader 会按顺序读取,当多个 Reader 都返回 EOF 之后,单独的 Reader 才返回 EOF,否则返回读取过程中遇到的任何错误。

r1 := strings.NewReader("Hello World!")    r2 := strings.NewReader("ABCDEFG")    r3 := strings.NewReader("abcdefg")    b := make([]byte, 15)    mr := io.MultiReader(r1, r2, r3)    for n, err := 0, error(nil); err == nil; {        n, err = mr.Read(b)        fmt.Printf("%q\n", b[:n])    }    // "Hello World!"    // "ABCDEFG"    // "abcdefg"    // ""    r1.Seek(0, 0)    r2.Seek(0, 0)    r3.Seek(0, 0)    mr = io.MultiReader(r1, r2, r3)    io.Copy(os.Stdout, mr) // output: Hello World!ABCDEFGabcdefg

TeeReader

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

TeeReader 对 r 进行封装,使 r 在读取数据的同时,自动向 w 中写入数据。它是一个无缓冲的 Reader,所以对 w 的写入操作必须在 r 的 Read 操作结束之前完成。所有写入时遇到的错误都会被作为 Read 方法的 err 返回。

r := strings.NewReader("Hello World!")    b := make([]byte, 15)    tr := io.TeeReader(r, os.Stdout)  // 会在屏幕输出    n, err := tr.Read(b)                  // output: Hello World!    fmt.Printf("\n%s   %v\n", b[:n], err) //output: Hello World!   

WriteString

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))}

WriteString 将字符串 s 写入到 w 中,返回写入的字节数和遇到的错误。

如果 w 实现了 WriteString 方法,则优先使用该方法将 s 写入 w 中。否则,将 s 转换为 []byte,然后调用 w.Write 方法将数据写入 w 中。

io.WriteString(os.Stdout, "Hello World!\n") // output:  Hello World!

MultiWriter

func MultiWriter(writers ...Writer) Writer {    allWriters := make([]Writer, 0, len(writers))    for _, w := range writers {        if mw, ok := w.(*multiWriter); ok {            allWriters = append(allWriters, mw.writers...)        } else {            allWriters = append(allWriters, w)        }    }    return &multiWriter{allWriters}}

MultiReader 将向自身写入的数据同步写入到所有 writers 中。

r := strings.NewReader("Hello World!\n")    mw := io.MultiWriter(os.Stdout, os.Stdout, os.Stdout)    r.WriteTo(mw)     // output: Hello World!    // output: Hello World!    // output: Hello World!

复制

CopyN

func CopyN(dst Writer, src Reader, n int64) (written int64, err error) {    written, err = Copy(dst, LimitReader(src, n))    if written == n {        return n, nil    }    if written < n && err == nil {        // src stopped early; must have been EOF.        err = EOF    }    return}

CopyN 从 src 中复制 n 个字节的数据到 dst 中,返回复制的字节数和遇到的错误。

  • 只有当 written = n 时,err 才返回 nil。

如果 dst 实现了 ReadFrom 方法,则优先调用该方法执行复制操作。

r := strings.NewReader("Hello World!")    n, err := io.CopyN(os.Stdout, r, 5) // output:Hello    fmt.Printf("\n%d   %v\n\n", n, err) // output:5   

CopyBuffer

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)}

CopyBuffer 相当于 Copy,只不过Copy 在执行的过程中会创建一个临时的缓冲区来中转数据,而 CopyBuffer 则可以单独提供一个缓冲区,让多个复制操作共用同一个缓冲区,避免每次复制操作都创建新的缓冲区。如果 buf == nil,则 CopyBuffer 会自动创建缓冲区。

r := strings.NewReader("Hello World!")    buf := make([]byte, 32)    n, err := io.CopyBuffer(os.Stdout, r, buf) // output: Hello World!    fmt.Printf("\n%d   %v\n", n, err)  // output: 12   

Copy


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

Copy 从 src 中复制数据到 dst 中,直到所有数据都复制完毕,返回复制的字节数和遇到的错误。

如果复制过程成功结束,则 err 返回 nil,而不是 EOF,因为 Copy 的定义为“直到所有数据都复制完毕”,所以不会将 EOF 视为错误返回。

如果 src 实现了 WriteTo 方法,则调用 src.WriteTo(dst) 复制数据,否则如果 dst 实现了 ReadeFrom 方法,则调用 dst.ReadeFrom(src) 复制数据。

r := strings.NewReader("Hello World!")    n, err := io.Copy(os.Stdout, r)      // output: Hello World!    fmt.Printf("\n%d   %v\n\n", n, err) // output:  12   

4. 结构体

SectionReader

type SectionReader struct {    r     ReaderAt    base  int64    off   int64    limit int64}

实现了 Read, Seek, and ReadAt 接口

NewSectionReader

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

NewSectionReader 对 r 进行封装,使其只能从 off 位置开始读取,最多只能读取 n个字节的的数据。相当于对 r 做了一个切片 r[off:off+n] 返回。底层实现是一个 *SectionReader。

Size

func (s *SectionReader) Size() int64 { return s.limit - s.base }

Size 返回允许读取部分的大小(即切片的长度 n)

使用示例

r := strings.NewReader("Hello World!")    sr := io.NewSectionReader(r, 6, 5)    n, err := io.Copy(os.Stdout, sr)                  // output: World    fmt.Printf("\n%d   %d   %v\n", sr.Size(), n, err) // output: 5   5   

LimitedReader

type LimitedReader struct {    R Reader // underlying reader    N int64  // max bytes remaining}

实现Read接口

使用示例

r := strings.NewReader("Hello World!")    sr := io.LimitedReader{r,2}    buf := make ([]byte,12)    n, err := sr.Read(buf)    fmt.Printf("\n%d  %s   %v\n", n,buf[:n], err) // output: 2  He   

teeReader

type teeReader struct {    r Reader    w Writer}

结合TeeReader函数使用


5. 备注


  1. EOF is the error returned by Read when no more input is available.Functions should return EOF only to signal a graceful end of input.If the EOF occurs unexpectedly in a structured data stream,the appropriate error is either ErrUnexpectedEOF or some other error

  2. ErrUnexpectedEOF means that EOF was encountered in the middle of reading a fixed-size block or data structure.

  3. EOF is the error returned by Read when no more input is available.Functions should return EOF only to signal a graceful end of input.If the EOF occurs unexpectedly in a structured data stream,the appropriate error is either ErrUnexpectedEOF or some other error

  4. ErrUnexpectedEOF means that EOF was encountered in the middle of reading a fixed-size block or data structure.

转载于:https://www.cnblogs.com/dust90/p/11288435.html

你可能感兴趣的文章
centos6.8下安装matlab2009(图片转帖)
查看>>
Mongo自动备份
查看>>
求助大神!怎样批量删除数据库表中某个字段中同样的一段字符!
查看>>
VMWARE虚拟机无法访问的三种方法分析
查看>>
enq: SQ - contention
查看>>
cer证书签名验证
查看>>
ant 安装
查看>>
新手Python第一天(接触)
查看>>
vue路由动态加载
查看>>
【原】UIWebView加载本地pdf、doc等文件
查看>>
iOS中ARC内部原理
查看>>
【bzoj1029】[JSOI2007]建筑抢修
查看>>
synchronized
查看>>
你不得不了解的应用容器引擎---Docker
查看>>
easyui datagrid 弹出页面会出现两个上下滚动条处理办法!
查看>>
迭代器和生成器
查看>>
MYSQL分区表功能测试简析
查看>>
codevs 1080 线段树练习
查看>>
JS模块化库seajs体验
查看>>
Android内核sysfs中switch类使用实例
查看>>