zoukankan      html  css  js  c++  java
  • go标准库的学习-io

    参考https://studygolang.com/pkgdoc

    导入方式:

    import "io"

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

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

    ⚠️os 包有三个可用变量 os.Stdout ,os.Stdin 和 os.Stderr ,其中os.Stdout 和 os.Stderr实现了io.Writer,所以其可以使用Write();os.Stdin 实现了io.Reader,所以可以使用Read()

     

    1.var 变量

    var EOF = errors.New("EOF")

    EOF当无法得到更多输入时,Read方法返回EOF。当函数一切正常的到达输入的结束时,就应返回EOF。如果在一个结构化数据流中EOF在不期望的位置出现了,则应返回错误ErrUnexpectedEOF或者其它给出更多细节的错误。

    var ErrUnexpectedEOF = errors.New("unexpected EOF")

    ErrUnexpectedEOF表示在读取一个固定尺寸的块或者数据结构时,在读取未完全时遇到了EOF。

    2.接口

    1)type Reader

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

    Reader接口用于包装基本的读取方法。

    Read()方法读取len(p)字节数据写入p。

    它返回写入的字节数和遇到的任何错误。即使Read方法返回值n < len(p),本方法在被调用时仍可能使用p的全部长度作为暂存空间。如果有部分可用数据,但不够len(p)字节,Read按惯例会返回可以读取到的数据,而不是等待更多数据。

    当Read在读取n > 0个字节后遭遇错误或者到达文件结尾时,会返回读取的字节数。它可能会在该次调用返回一个非nil的错误,或者在下一次调用时返回0和该错误。一个常见的例子,Reader接口会在输入流的结尾返回非0的字节数,返回值err == EOF或err == nil。但不管怎样,下一次Read调用必然返回(0, EOF)。调用者应该总是先处理读取的n > 0字节再处理错误值。这么做可以正确的处理发生在读取部分数据后的I/O错误,也能正确处理EOF事件。

    如果Read的某个实现返回0字节数和nil错误值,表示被阻碍;调用者应该将这种情况视为未进行操作。

    举例:

    package main 
    import(
        "fmt"
        "io"
        "strings"
        "os"
    )
    func main() {
        reader := strings.NewReader("test reader's usage")
        buf := make([]byte, 4) //生成一个能够存放4 bytes数据的数组
        for {//无限循环直至有错或数据读取完返回EOF
            count, err := reader.Read(buf)//后面读取的内容会覆盖前面的buf的内容
            if err != nil {
                if err == io.EOF {
                    fmt.Println("EOF : ", count)
                    break
                }
                fmt.Println(err)
                os.Exit(1)
            }
            fmt.Println(count, string(buf[:count]))
        }
    }

    这里使用的是strings.NewReader,他会返回一个Reader

    返回:

    userdeMBP:src user$ go run test.go
    4 test
    4  rea
    4 der'
    4 s us
    3 age
    EOF :  0

    当然你也可以实现一个你自己的Reader,实现从流中删除空格和换行符

    package main 
    import(
        "fmt"
        "io"
        "os"
    )
    func main() {
        reader := newNoSpaceReader("test my own reader's usage
    ,use to delete the space
    ")
        buf := make([]byte, 4) //生成一个能够存放4 bytes数据的数组
        for{//无限循环直至有错或数据读取完返回EOF
            count, err := reader.Read(buf)//后面读取的内容会覆盖前面的buf的内容
            if err != nil {
                if err == io.EOF {
                    fmt.Println()
                    fmt.Println("EOF : ", count)
                    break
                }
                fmt.Println(err)
                os.Exit(1)
            }
            fmt.Print(string(buf[:count]))
        }
    }
    
    type NoSpaceReader struct{ //删除字符串中的空格和换行符
        content string //输入的字符串
        cur int        //当前读取到的位置
    }
    
    func newNoSpaceReader(src string) *NoSpaceReader { return &NoSpaceReader{src, 0}}
    
    func (n * NoSpaceReader) Read(p []byte) (int, error) {
        //当前读取位置 >=字符串长度,说明已经读取完了,返回EOF
        if n.cur >= len(n.content) {
            return 0, io.EOF
        }
        var number int = 0
        for _, char := range n.content[n.cur:] {
            if char != 32 && char != 10 {//32为空格,10为换行符
                number++
                if number > len(p){ //如果读取的字符数超过了p的长度,则停止并返回
                    return len(p), nil
                }        
                p[number-1] = byte(char)
            }
            n.cur++
        }
        return number, nil
    }

    更简单的方法:

    package main 
    import(
        "fmt"
        "io"
        "os"
        "strings"
    )
    func main() {
        reader := newNoSpaceReader(strings.NewReader("test my own reader's usage
    ,use to delete the space
    "))
        buf := make([]byte, 4) //生成一个能够存放4 bytes数据的数组
        for{//无限循环直至有错或数据读取完返回EOF
            count, err := reader.Read(buf)//后面读取的内容会覆盖前面的buf的内容
            if err != nil {
                if err == io.EOF {
                    fmt.Println()
                    fmt.Println("EOF : ", count)
                    break
                }
                fmt.Println(err)
                os.Exit(1)
            }
            fmt.Print(string(buf[:count]))
        }
    }
    
    type NoSpaceReader struct{ //删除字符串中的空格和换行符
        reader io.Reader
    }
    
    func newNoSpaceReader(reader io.Reader) *NoSpaceReader { return &NoSpaceReader{reader}}
    
    func (n * NoSpaceReader) Read(p []byte) (int, error) {
        count, err := n.reader.Read(p)
        if err != nil {
            return count, err
        }
        temp := make([]byte, count)
        var i int = 0
        for _, char := range p[:count] {
            if char != 32 && char != 10 {//32为空格,10为换行符       
                temp[i] = char
                i++
            }
    
        }
        copy(p, temp)
        return count, nil
    }

    当然我们知道strings标准库中有实现上面的要求的方法,这只是一个示范,等价于:

    package main 
    import(
        "fmt"
        "strings"
    )
    func main() {
        str := "test my own reader's usage
    ,use to delete the space
    "
        str = strings.Replace(str, " ", "", -1)//去除str中的空格
        str = strings.Replace(str, "
    ", "", -1)//去除str中的换行符
        fmt.Println(str)
    }

    2)type Writer

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

    Writer接口用于包装基本的写入方法。

    Write方法len(p) 字节数据从p写入底层的数据流。它会返回写入的字节数(0 <= n <= len(p))和遇到的任何导致写入提取结束的错误。Write必须返回非nil的错误,如果它返回的 n < len(p)。Write不能修改切片p中的数据,即使临时修改也不行。

     举例:

    package main 
    import(
        "fmt"
        "bytes"
        "log"
    )
    func main() {
        var writer bytes.Buffer
        strings := []string{
            "hello ",
            "right now i am testing the usage of ",
            "writer",
        }
        for _, s := range strings{
            n, err :=writer.Write([]byte(s))
            if err != nil{
                log.Fatal(err)
            }
            if n != len(s){
                log.Fatal("fail to write the right string")
            }
        }
        fmt.Println(writer.String())
    }

    bytes.Buffer实现了Writer接口

    返回:

    userdeMBP:src user$ go run test.go
    hello right now i am testing the usage of writer

    3)type Closer

    type Closer interface {
        Close() error
    }

    Closer接口用于包装基本的关闭方法。

    在第一次调用之后再次被调用时,Close方法的的行为是未定义的。某些实现可能会说明他们自己的行为。

     

    4)type Seeker

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

    Seeker接口用于包装基本的移位方法。

    Seek方法设定下一次读写的位置:偏移量为offset,校准点由whence确定:0表示相对于文件起始;1表示相对于当前位置;2表示相对于文件结尾。Seek方法返回新的位置以及可能遇到的错误。

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

     

    5)type ReadCloser

    type ReadCloser interface {
        Reader
        Closer
    }

    ReadCloser接口聚合了基本的读取和关闭操作。

    6)type ReadSeeker

    type ReadSeeker interface {
        Reader
        Seeker
    }

    ReadSeeker接口聚合了基本的读取和移位操作。

    7)type WriteCloser

    type WriteCloser interface {
        Writer
        Closer
    }

    WriteCloser接口聚合了基本的写入和关闭操作。

    8)type WriteSeeker

    type WriteSeeker interface {
        Writer
        Seeker
    }

    WriteSeeker接口聚合了基本的写入和移位操作。

    9)type ReadWriter

    type ReadWriter interface {
        Reader
        Writer
    }

    ReadWriter接口聚合了基本的读写操作。

     

    10)type ReadWriteCloser

    type ReadWriteCloser interface {
        Reader
        Writer
        Closer
    }

    ReadWriteCloser接口聚合了基本的读写和关闭操作。

    11)type ReadWriteSeeker

    type ReadWriteSeeker interface {
        Reader
        Writer
        Seeker
    }

    ReadWriteSeeker接口聚合了基本的读写和移位操作。

    12)type ReaderAt

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

    ReaderAt接口包装了基本的ReadAt方法。

    ReadAt从底层输入流的偏移量off位置读取len(p)字节数据写入p, 它返回读取的字节数(0 <= n <= len(p))和遇到的任何错误。当ReadAt方法返回值n < len(p)时,它会返回一个非nil的错误来说明为啥没有读取更多的字节。在这方面,ReadAt是比Read要严格的

    即使ReadAt方法返回值 n < len(p),它在被调用时仍可能使用p的全部长度作为暂存空间。如果有部分可用数据,但不够len(p)字节,ReadAt会阻塞直到获取len(p)个字节数据或者遇到错误。在这方面,ReadAt和Read是不同的。

    如果ReadAt返回时到达输入流的结尾,而返回值n == len(p),其返回值err既可以是EOF也可以是nil。

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

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

     

    13)type WriterAt

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

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

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

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

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

     

    14)type ByteReader

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

    ByteReader是基本的ReadByte方法的包装。

    ReadByte读取输入中的单个字节并返回。如果没有字节可读取,会返回错误。

     

    15)type ByteScanner

    type ByteScanner interface {
        ByteReader
        UnreadByte() error
    }

    ByteScanner接口在基本的ReadByte方法之外还添加了UnreadByte方法。

    UnreadByte方法让下一次调用ReadByte时返回之前调用ReadByte时返回的同一个字节。连续调用两次UnreadByte方法而中间没有调用ReadByte时,可能会导致错误。

    16)type RuneReader

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

    RuneReader是基本的ReadRune方法的包装。

    ReadRune读取单个utf-8编码的字符,返回该字符和它的字节长度。如果没有有效的字符,会返回错误。

     

    17)type RuneScanner

    type RuneScanner interface {
        RuneReader
        UnreadRune() error
    }

    RuneScanner接口在基本的ReadRune方法之外还添加了UnreadRune方法。

    UnreadRune方法让下一次调用ReadRune时返回之前调用ReadRune时返回的同一个utf-8字符。连续调用两次UnreadRune方法而中间没有调用ReadRune时,可能会导致错误。

     

    18)type ByteWriter

    type ByteWriter interface {
        WriteByte(c byte) error
    }

    ByteWriter是基本的WriteByte方法的包装。

    19)type ReaderFrom

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

    ReaderFrom接口包装了基本的ReadFrom方法。

    ReadFrom方法从r读取数据直到EOF或者遇到错误。返回值n是读取的字节数,执行时遇到的错误(EOF除外)也会被返回。

     

    20)type WriterTo

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

    WriterTo接口包装了基本的WriteTo方法。

    WriteTo方法将数据写入w直到没有数据可以写入或者遇到错误。返回值n是写入的字节数,执行时遇到的任何错误也会被返回。

     

    3.接口调用

    1)type LimitedReader

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

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

    1> func LimitReader

    func LimitReader(r Reader, n int64) Reader

    返回一个Reader,它从r中读取n个字节后以EOF停止。返回值接口的底层为*LimitedReader类型。

    实现源码:

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

    2> func (l *LimitedReader) Read

    func (l *LimitedReader) Read(p []byte) (n int, err error)

    说明调用了Reader接口

    实现源码:

    func (l *LimitedReader) Read(p []byte) (n int, err error) {
        if l.N <= 0 {
            return 0, EOF
        }
        if int64(len(p)) > l.N {
            p = p[0:l.N]
        }
        n, err = l.R.Read(p)
        l.N -= int64(n)
        return
    }

    2)type SectionReader

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

    SectionReader实现了对底层满足ReadAt接口的输入流某个片段的Read、ReadAt、Seek方法。

    1> func NewSectionReader

    func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader

    返回一个从r中的偏移量off处为起始,读取n个字节后以EOF停止的SectionReader。

    实现源码:

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

    2> func (*SectionReader) Size

    func (s *SectionReader) Size() int64

    Size返回该片段的字节数。

    实现源码:

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

    3> func (*SectionReader) Read

    func (s *SectionReader) Read(p []byte) (n int, err error)

    实现源码:

    func (s *SectionReader) Read(p []byte) (n int, err error) {
        if s.off >= s.limit {
            return 0, EOF
        }
        if max := s.limit - s.off; int64(len(p)) > max {
            p = p[0:max]
        }
        n, err = s.r.ReadAt(p, s.off)
        s.off += int64(n)
        return
    }

    4> func (*SectionReader) ReadAt

    func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error)

    实现代码:

    func (s *SectionReader) ReadAt(p []byte, off int64) (n int, err error) {
        if off < 0 || off >= s.limit-s.base {
            return 0, EOF
        }
        off += s.base
        if max := s.limit - off; int64(len(p)) > max {
            p = p[0:max]
            n, err = s.r.ReadAt(p, off)
            if err == nil {
                err = EOF
            }
            return n, err
        }
        return s.r.ReadAt(p, off)
    }

    5> func (*SectionReader) Seek

    func (s *SectionReader) Seek(offset int64, whence int) (int64, error)

    实现源码:

    func (s *SectionReader) Seek(offset int64, whence 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
    }

    3)type PipeReader

    首先是pipe结构体的实现:

    type pipe struct {
        wrMu sync.Mutex // Serializes Write operations
        wrCh chan []byte
        rdCh chan int
    
        once sync.Once // Protects closing done
        done chan struct{}
        rerr atomicError
        werr atomicError
    }
    
    func (p *pipe) Read(b []byte) (n int, err error) {
        select {
        case <-p.done:
            return 0, p.readCloseError()
        default:
        }
    
        select {
        case bw := <-p.wrCh:
            nr := copy(b, bw)
            p.rdCh <- nr
            return nr, nil
        case <-p.done:
            return 0, p.readCloseError()
        }
    }
    
    func (p *pipe) readCloseError() error {
        rerr := p.rerr.Load()
        if werr := p.werr.Load(); rerr == nil && werr != nil {
            return werr
        }
        return ErrClosedPipe
    }
    
    func (p *pipe) CloseRead(err error) error {
        if err == nil {
            err = ErrClosedPipe
        }
        p.rerr.Store(err)
        p.once.Do(func() { close(p.done) })
        return nil
    }
    
    func (p *pipe) Write(b []byte) (n int, err error) {
        select {
        case <-p.done:
            return 0, p.writeCloseError()
        default:
            p.wrMu.Lock()
            defer p.wrMu.Unlock()
        }
    
        for once := true; once || len(b) > 0; once = false {
            select {
            case p.wrCh <- b:
                nw := <-p.rdCh
                b = b[nw:]
                n += nw
            case <-p.done:
                return n, p.writeCloseError()
            }
        }
        return n, nil
    }
    
    func (p *pipe) writeCloseError() error {
        werr := p.werr.Load()
        if rerr := p.rerr.Load(); werr == nil && rerr != nil {
            return rerr
        }
        return ErrClosedPipe
    }
    
    func (p *pipe) CloseWrite(err error) error {
        if err == nil {
            err = EOF
        }
        p.werr.Store(err)
        p.once.Do(func() { close(p.done) })
        return nil
    }

    初始化pipe:

    func Pipe() (*PipeReader, *PipeWriter) {
        p := &pipe{
            wrCh: make(chan []byte),
            rdCh: make(chan int),
            done: make(chan struct{}),
        }
        return &PipeReader{p}, &PipeWriter{p}
    }

    type PipeReader

    type PipeReader struct {
        p *pipe
    }

    PipeReader是一个管道的读取端。

    1> func Pipe

    func Pipe() (*PipeReader, *PipeWriter)

    Pipe创建一个同步的内存中的管道。它可以用于连接期望io.Reader的代码和期望io.Writer的代码。一端的读取对应另一端的写入,直接在两端拷贝数据,没有内部缓冲。可以安全的并行调用Read和Write或者Read/Write与Close方法。Close方法会在最后一次阻塞中的I/O操作结束后完成。并行调用Read或并行调用Write也是安全的:每一个独立的调用会依次进行。

    os标准库中也有该方法

    2> func (*PipeReader) Read

    func (r *PipeReader) Read(data []byte) (n int, err error)

    Read实现了标准Reader接口:它从管道中读取数据,会阻塞直到写入端开始写入或写入端被关闭。

    实现源码:

    func (r *PipeReader) Read(data []byte) (n int, err error) {
        return r.p.Read(data)
    }

    3> func (*PipeReader) Close

    func (r *PipeReader) Close() error

    Close关闭读取器;关闭后如果对管道的写入端进行写入操作,就会返回(0, ErrClosedPip)。

    func (r *PipeReader) Close() error {
        return r.CloseWithError(nil)
    }
    
    func (r *PipeReader) CloseWithError(err error) error {
        return r.p.CloseRead(err)

    4> func (*PipeReader) CloseWithError

    func (r *PipeReader) CloseWithError(err error) error

    CloseWithError类似Close方法,但将调用Write时返回的错误改为err。

    4)type PipeWriter

    type PipeWriter struct {
        p *pipe
    }

    PipeWriter是一个管道的写入端。

    1> func (*PipeWriter) Write

    func (w *PipeWriter) Write(data []byte) (n int, err error)

    Write实现了标准Writer接口:它将数据写入到管道中,会阻塞直到读取器读完所有的数据或读取端被关闭。

    实现代码:

    func (w *PipeWriter) Write(data []byte) (n int, err error) {
        return w.p.Write(data)
    }

    2> func (*PipeWriter) Close

    func (w *PipeWriter) Close() error

    Close关闭写入器;关闭后如果对管道的读取端进行读取操作,就会返回(0, EOF)。

    实现代码:

    func (w *PipeWriter) Close() error {
        return w.CloseWithError(nil)
    }
    
    // CloseWithError closes the writer; subsequent reads from the
    // read half of the pipe will return no bytes and the error err,
    // or EOF if err is nil.
    //
    // CloseWithError always returns nil.
    func (w *PipeWriter) CloseWithError(err error) error {
        return w.p.CloseWrite(err)
    }

    3> func (*PipeWriter) CloseWithError

    func (w *PipeWriter) CloseWithError(err error) error

    CloseWithError类似Close方法,但将调用Read时返回的错误改为err。

    5)type TeeReader

    type teeReader struct {
        r Reader
        w Writer
    }

    TeeReader返回一个将其从r读取的数据写入w的Reader接口。所有通过该接口对r的读取都会执行对应的对w的写入。没有内部的缓冲:写入必须在读取完成前完成。写入时遇到的任何错误都会作为读取错误返回。

    1> func TeeReader —— 初始化

    func TeeReader(r Reader, w Writer) Reader

    实现源码:

    func TeeReader(r Reader, w Writer) Reader {
        return &teeReader{r, w}
    }
    
    func (t *teeReader) Read(p []byte) (n int, err error) {
        n, err = t.r.Read(p)
        if n > 0 {
            if n, err := t.w.Write(p[:n]); err != nil {
                return n, err
            }
        }
        return
    }

    6)type MultiReader

    type multiReader struct {
        readers []Reader
    }

    1>func MultiReader

    func MultiReader(readers ...Reader) Reader

    MultiReader返回一个将提供的Reader在逻辑上串联起来的Reader接口。他们依次被读取。当所有的输入流都读取完毕,Read才会返回EOF。如果readers中任一个返回了非nil非EOF的错误,Read方法会返回该错误。

    初始化:

    func MultiReader(readers ...Reader) Reader {
        r := make([]Reader, len(readers))
        copy(r, readers)
        return &multiReader{r}
    }
    
    func (mr *multiReader) Read(p []byte) (n int, err error) {
        for len(mr.readers) > 0 {
            // Optimization to flatten nested multiReaders (Issue 13558).
            if len(mr.readers) == 1 {
                if r, ok := mr.readers[0].(*multiReader); ok {
                    mr.readers = r.readers
                    continue
                }
            }
            n, err = mr.readers[0].Read(p)
            if err == EOF {
                // Use eofReader instead of nil to avoid nil panic
                // after performing flatten (Issue 18232).
                mr.readers[0] = eofReader{} // permit earlier GC
                mr.readers = mr.readers[1:]
            }
            if n > 0 || err != EOF {
                if err == EOF && len(mr.readers) > 0 {
                    // Don't return EOF yet. More readers remain.
                    err = nil
                }
                return
            }
        }
        return 0, EOF
    }

    7)type MultiWriter

    type multiWriter struct {
        writers []Writer
    }

    1> func MultiWriter

    func MultiWriter(writers ...Writer) Writer

    MultiWriter创建一个Writer接口,会将提供给其的数据写入所有创建时提供的Writer接口。

    初始化源码:

    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}
    }
    
    func (t *multiWriter) Write(p []byte) (n int, err error) {
        for _, w := range t.writers {
            n, err = w.Write(p)
            if err != nil {
                return
            }
            if n != len(p) {
                err = ErrShortWrite
                return
            }
        }
        return len(p), nil
    }
    
    var _ StringWriter = (*multiWriter)(nil)
    
    func (t *multiWriter) WriteString(s string) (n int, err error) {
        var p []byte // lazily initialized if/when needed
        for _, w := range t.writers {
            if sw, ok := w.(StringWriter); ok {
                n, err = sw.WriteString(s)
            } else {
                if p == nil {
                    p = []byte(s)
                }
                n, err = w.Write(p)
            }
            if err != nil {
                return
            }
            if n != len(s) {
                err = ErrShortWrite
                return
            }
        }
        return len(s), nil
    }

    4.其他函数

    1)func WriteString

    func WriteString(w Writer, s string) (n int, err error)

    WriteString函数将字符串s的内容写入w中。如果w已经实现了WriteString方法,函数会直接调用该方法。

    实现源码:

    func WriteString(w Writer, s string) (n int, err error) {
        if sw, ok := w.(StringWriter); ok {
            return sw.WriteString(s) //如果是StringWriter接口,则调用自己实现的WriteString方法
        }
        return w.Write([]byte(s)) //否则就先将string转换成[]byte类型,然后再调用Write()方法
    }

    举例:

    package main 
    import(
        "fmt"
        "io"
        "log"
        "os"
    )
    func main() {
        str :="now is to test io' function,let's go
    "
        WriteNumber, err := io.WriteString(os.Stdout, str)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(WriteNumber)
    }

    返回:

    userdeMBP:src user$ go run test.go
    now is to test io' function,let's go
    37

    2)func ReadAtLeast

    func ReadAtLeast(r Reader, buf []byte, min int) (n int, err error)

    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) {
        if len(buf) < min {
            return 0, ErrShortBuffer
        }
        for n < min && err == nil {
            var nn int
            nn, err = r.Read(buf[n:])
            n += nn
        }
        if n >= min {
            err = nil
        } else if n > 0 && err == EOF {
            err = ErrUnexpectedEOF
        }
        return
    }

     举例:

    1^

    package main 
    import(
        "fmt"
        "io"
        "log"
        "strings"
    )
    func main() {
        reader := strings.NewReader("now is to test io' function,let's go
    ")
        data := make([]byte, 25)
        readNumber, err := io.ReadAtLeast(reader, data, 20)//说是至少20,但是既然data的长度是25,那肯定还是会读取25长度的数据
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(string(data[:readNumber]))
        fmt.Println(readNumber)
    }

    返回:

    userdeMBP:src user$ go run test.go
    now is to test io' functi
    25

    2^该函数和下面函数的不同在于,如果data的长度大于数据的长度,min的长度不大于数据长度时,不会报错,而是读取最长的数据,如改成:

    data := make([]byte, 45)

    返回:

    userdeMBP:src user$ go run test.go
    now is to test io' function,let's go
    
    37

    3^但是如果min的长度也大于数据的长度,如min改成40,则报错:

    userdeMBP:src user$ go run test.go
    2019/01/25 15:25:27 unexpected EOF
    exit status 1

    4^如果min的长度大于data,会报错:

        data := make([]byte, 25)
        readNumber, err := io.ReadAtLeast(reader, data, 30)

    返回:

    userdeMBP:src user$ go run test.go
    2019/01/25 15:27:24 short buffer
    exit status 1

    3)func ReadFull

    func ReadFull(r Reader, buf []byte) (n int, err error)

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

     举例:

    package main 
    import(
        "fmt"
        "io"
        "log"
        "strings"
    )
    func main() {
        reader := strings.NewReader("now is to test io.Copy,let's go
    ")
        data := make([]byte, 30)
        readNumber, err := io.ReadFull(reader, data)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(string(data[:readNumber]))
        fmt.Println(readNumber)
    }

    返回:

    userdeMBP:src user$ go run test.go
    now is to test io.Copy,let's g
    30

    如果读取不足,比如将上面的数组的大小从30改到34,大于reader中内容的长度,则会返回:

    userdeMBP:src user$ go run test.go
    2019/01/25 15:18:46 unexpected EOF
    exit status 1

    4)func CopyN

    func CopyN(dst Writer, src Reader, n int64) (written int64, err error)

    从src拷贝n个字节数据到dst,直到在src上到达EOF或发生错误。返回复制的字节数和遇到的第一个错误。

    只有err为nil时,written才会等于n。如果dst实现了ReaderFrom接口,本函数很调用它实现拷贝。

    实现源码:

    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
    }

     举例:

    package main 
    import(
        "fmt"
        "io"
        "os"
        "log"
        "strings"
    )
    func main() {
        reader := strings.NewReader("now is to test io.Copy,let's go
    ")
        writtenNumber, err := io.CopyN(os.Stdout, reader, 30)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println()
        fmt.Println(writtenNumber)
    }

    返回:

    userdeMBP:src user$ go run test.go
    now is to test io.Copy,let's g
    30

    5)func Copy

    func Copy(dst Writer, src Reader) (written int64, err error)

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

    举例:

    package main 
    import(
        "fmt"
        "io"
        "os"
        "log"
        "strings"
    )
    func main() {
        reader := strings.NewReader("now is to test io.Copy,let's go
    ")
        writtenNumber, err := io.Copy(os.Stdout, reader)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(writtenNumber)
    }

    返回:

    userdeMBP:src user$ go run test.go
    now is to test io.Copy,let's go
    32

    源码没注释 

     

  • 相关阅读:
    CORS跨域漏洞学习
    CVE-2020-0796漏洞复现(RCE)
    Wfuzz使用学习
    DNSlog注入学习
    一些CTF练习记录
    数据结构与算法(十三):赫夫曼树
    数据结构与算法(十二):堆排序
    博客园自定义代码块样式
    Nginx入门(二):常用功能配置
    数据结构与算法(十一):二叉树
  • 原文地址:https://www.cnblogs.com/wanghui-garcia/p/10314495.html
Copyright © 2011-2022 走看看