• 标准库之bufio


    1、概述

    bufio 包实现了缓存IO。它包装了 io.Reader 和 io.Writer 对象,创建了另外的Reader和Writer对象,它们也实现了 io.Reader 和 io.Writer 接口,不过它们是有缓存的。该包同时为文本I/O提供了一些便利操作。

    2、Reader 类型和方法

    bufio.Reader 结构包装了一个 io.Reader 对象,提供缓存功能,同时实现了 io.Reader 接口。

    Reader 结构没有任何导出的字段,结构定义如下:

        type Reader struct {
            buf          []byte        // 缓存
            rd           io.Reader    // 底层的io.Reader
            // r:从buf中读走的字节(偏移);w:buf中填充内容的偏移;
            // w - r 是buf中可被读的长度(缓存数据的大小),也是Buffered()方法的返回值
            r, w         int
            err          error        // 读过程中遇到的错误
            lastByte     int        // 最后一次读到的字节(ReadByte/UnreadByte)
            lastRuneSize int        // 最后一次读到的Rune的大小 (ReadRune/UnreadRune)
        }
    

    2.1、实例化 bufio.Reader 

    bufio 包提供了两个实例化 bufio.Reader 对象的函数:NewReader 和 NewReaderSize。其中,NewReader 函数是调用 NewReaderSize 函数实现的:

        func NewReader(rd io.Reader) *Reader {
            // 默认缓存大小:defaultBufSize=4096
            return NewReaderSize(rd, defaultBufSize)
        }
    

    NewReaderSize的源码:

        func NewReaderSize(rd io.Reader, size int) *Reader {
            // 已经是bufio.Reader类型,且缓存大小不小于 size,则直接返回
            b, ok := rd.(*Reader)
            if ok && len(b.buf) >= size {
                return b
            }
            // 缓存大小不会小于 minReadBufferSize (16字节)
            if size < minReadBufferSize {
                size = minReadBufferSize
            }
            // 构造一个bufio.Reader实例
            return &Reader{
                buf:          make([]byte, size),
                rd:           rd,
                lastByte:     -1,
                lastRuneSize: -1,
            }
        }
    

    2.2、ReadSlice、ReadBytes、ReadString 和 ReadLine 方法

    参考:bufio — 缓存 IO · Go语言标准库 (studygolang.com)

      2.3、Peek方法

    从方法的名称可以猜到,该方法只是“窥探”一下 Reader 中没有读取的 n 个字节。好比栈数据结构中的取栈顶元素,但不出栈。

    方法的签名如下:

    func (b *Reader) Peek(n int) ([]byte, error)
    

    同上面介绍的 ReadSlice一样,返回的 []byte 只是 buffer 中的引用,在下次IO操作后会无效,可见该方法(以及ReadSlice这样的,返回buffer引用的方法)对多 goroutine 是不安全的,也就是在多并发环境下,不能依赖其结果。

      2.4其他方法

        func (b *Reader) Read(p []byte) (n int, err error)
        func (b *Reader) ReadByte() (c byte, err error)
        func (b *Reader) ReadRune() (r rune, size int, err error)
        func (b *Reader) UnreadByte() error
        func (b *Reader) UnreadRune() error
        func (b *Reader) WriteTo(w io.Writer) (n int64, err error)
    

    3、Scanner 类型和方法

    在 bufio 包中有多种方式获取文本输入,ReadBytes、ReadString 和独特的 ReadLine,对于简单的目的这些都有些过于复杂了。在 Go 1.1 中,添加了一个新类型,Scanner,以便更容易的处理如按行读取输入序列或空格分隔单词等,这类简单的任务。它终结了如输入一个很长的有问题的行这样的输入错误,并且提供了简单的默认行为:基于行的输入,每行都剔除分隔标识。

    3.1、Scanner 的实例化

    Scanner 没有导出任何字段,而它需要有外部的 io.Reader 对象,因此,我们不能直接实例化 Scanner 对象,必须通过 bufio 包提供的实例化函数来实例化。实例化函数签名以及内部实现:

        func NewScanner(r io.Reader) *Scanner {
            return &Scanner{
                r:            r,
                split:        ScanLines,
                maxTokenSize: MaxScanTokenSize,
                buf:          make([]byte, 4096), // Plausible starting size; needn't be large.
            }
        }
    

      3.2、Scanner 的Split 方法

    Split 方法 前面我们提到过可以通过 Split 方法为 Scanner 实例设置分词行为。由于 Scanner 实例的默认 split 总是 ScanLines,如果我们想要用其他的 split,可以通过 Split 方法做到。

    比如,我们想要统计一段英文有多少个单词(不排除重复),我们可以这么做:

        const input = "This is The Golang Standard Library.\nWelcome you!"
        scanner := bufio.NewScanner(strings.NewReader(input))
        scanner.Split(bufio.ScanWords)
        count := 0
        for scanner.Scan() {
            count++
        }
        if err := scanner.Err(); err != nil {
            fmt.Fprintln(os.Stderr, "reading input:", err)
        }
        fmt.Println(count)
    

    4、Writer 类型和方法

    bufio.Writer 结构包装了一个 io.Writer 对象,提供缓存功能,同时实现了 io.Writer 接口。

    Writer 结构没有任何导出的字段,结构定义如下:

        type Writer struct {
            err error        // 写过程中遇到的错误
            buf []byte        // 缓存
            n   int            // 当前缓存中的字节数
            wr  io.Writer    // 底层的 io.Writer 对象
        }
    

      4.1实例化

    和 Reader 类型一样,bufio 包提供了两个实例化 bufio.Writer 对象的函数:NewWriter 和 NewWriterSize。其中,NewWriter 函数是调用 NewWriterSize 函数实现的:

        func NewWriter(wr io.Writer) *Writer {
            // 默认缓存大小:defaultBufSize=4096
            return NewWriterSize(wr, defaultBufSize)
        }
    

     NewWriterSize 的源码:

        func NewWriterSize(wr io.Writer, size int) *Writer {
            // 已经是 bufio.Writer 类型,且缓存大小不小于 size,则直接返回
            b, ok := wr.(*Writer)
            if ok && len(b.buf) >= size {
                return b
            }
            if size <= 0 {
                size = defaultBufSize
            }
            return &Writer{
                buf: make([]byte, size),
                wr:  w,
            }
        }
    

    4.2Available 和 Buffered 方法

    Available 方法获取缓存中还未使用的字节数(缓存大小 - 字段 n 的值);Buffered 方法获取写入当前缓存中的字节数(字段 n 的值)。

      4.3Flush 方法

    该方法将缓存中的所有数据写入底层的 io.Writer 对象中。使用 bufio.Writer 时,在所有的 Write 操作完成之后,应该调用 Flush 方法使得缓存都写入 io.Writer 对象中。

      4.4其他方法

        // 实现了 io.ReaderFrom 接口
        func (b *Writer) ReadFrom(r io.Reader) (n int64, err error)
    
        // 实现了 io.Writer 接口
        func (b *Writer) Write(p []byte) (nn int, err error)
    
        // 实现了 io.ByteWriter 接口
        func (b *Writer) WriteByte(c byte) error
    
        // io 中没有该方法的接口,它用于写入单个 Unicode 码点,返回写入的字节数(码点占用的字节),内部实现会根据当前 rune 的范围调用 WriteByte 或 WriteString
        func (b *Writer) WriteRune(r rune) (size int, err error)
    
        // 写入字符串,如果返回写入的字节数比 len(s) 小,返回的error会解释原因
        func (b *Writer) WriteString(s string) (int, error)
    

    5、ReadWriter 类型和实例化

    ReadWriter 结构存储了 bufio.Reader 和 bufio.Writer 类型的指针(内嵌),它实现了 io.ReadWriter 结构。

        type ReadWriter struct {
            *Reader
            *Writer
        }
    

    ReadWriter 的实例化可以跟普通结构类型一样,也可以通过调用 bufio.NewReadWriter 函数来实现:只是简单的实例化 ReadWriter。

        func NewReadWriter(r *Reader, w *Writer) *ReadWriter {
            return &ReadWriter{r, w}
        }
    

      

    参考:bufio — 缓存 IO · Go语言标准库 (studygolang.com)

  • 相关阅读:
    转Asktom:Pipelined Functions
    转:PVE法师附魔
    附魔300375图纸掉落大全
    转:Oracle直方图详解
    转:JSON的序列化及GET异步调用.
    转:ORACLE 中dbms_stats的使用
    jQuery中$.each的用法
    魔兽世界天赋详解之 法师篇 一冰法
    Miley's Oracle讲堂第三课:如何在Oracle中使用对象表存储数据.
    台服体验之急速升级
  • 原文地址:https://www.cnblogs.com/mango1997/p/16305935.html
Copyright © 2020-2023  润新知