Go 語言標準庫 bufio 詳解

01 介紹

Go 語言標準庫 bufio 是基於 Go 語言標準庫 io 實現的,查看源碼可以發現,實際上它是包裝了 io.Reader 接口和 io.Writer 接口,並且實現它們。

bufio 顧名思義,就是在緩衝區讀寫數據,比直接讀寫文件或網絡中的數據,性能更好些。

本文我們介紹 bufio 的相關內容,建議讀者朋友們最好是先了解一下 io 的相關內容。

02 標準庫 bufio 的數據類型

查看標準庫 bufio 的文檔 [1],它的數據類型主要有 bufio.Readerbufio.Writerbufio.ReadWriterbufio.Scanner

我們以 bufio.Reader 爲例,介紹它的數據結構、初始化方式和提供的方法。

bufio.Reader 的數據結構:

type Reader struct {
 buf          []byte
 rd           io.Reader
 r, w         int
 err          error
 lastByte     int
 lastRuneSize int
}

閱讀源碼,我們可以發現 bufio.Reader 中包含的字段:

bufio.Reader 的初始化方式:

使用 bufio.Reader 時,需要先初始化,bufio 包提供了兩個初始化的函數,分別是 NewReaderSizeNewReader

func NewReaderSize(rd io.Reader, size int) *Reader {
 // Is it already a Reader?
 b, ok := rd.(*Reader)
 if ok && len(b.buf) >= size {
  return b
 }
 if size < minReadBufferSize {
  size = minReadBufferSize
 }
 r := new(Reader)
 r.reset(make([]byte, size), rd)
 return r
}

func NewReader(rd io.Reader) *Reader {
 return NewReaderSize(rd, defaultBufSize)
}

閱讀源碼,我們可以發現這兩個函數的返回值都是 *bufio.Reader 類型。

其中 NewReader 是包裝了 NewReaderSize 函數,給定了一個默認值 4096,設置讀緩衝區的大小。

如果我們使用默認值,一般選擇使用 NewReader 函數。

如果不想使用默認值,可以選擇使用 NewReaderSize 函數。

bufio.Reader 提供的方法:

bufio.Reader 提供了 15 個方法,我們介紹兩個比較常用的方法,分別是 ReadReadBytes

func (b *Reader) Read(p []byte) (n int, err error) {
 // 省略代碼 ...
 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()
  }
  // 省略代碼 ...
  b.w += n
 }

 // copy as much as we can
 // Note: if the slice panics here, it is probably because
 // the underlying reader returned a bad count. See issue 49795.
 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
}

閱讀源碼,我們可以發現 Read 方法是將緩衝區中的數據,讀取到 p 中,並返回讀取的字節大小和錯誤。

func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
 full, frag, n, err := b.collectFragments(delim)
 // Allocate new buffer to hold the full pieces and the fragment.
 buf := make([]byte, n)
 n = 0
 // Copy full pieces and fragment in.
 for i := range full {
  n += copy(buf[n:], full[i])
 }
 copy(buf[n:], frag)
 return buf, err
}

閱讀源碼,我們可以發現 ReadBytes 方法是讀取緩衝區中的數據截止到分隔符 delim 的位置,並返回數據和錯誤。

使用示例:

Read 方法

func main() {
 f, _ := os.Open("/Users/frank/GolandProjects/go-package/lesson14/file.txt")
 defer f.Close()
 r := bufio.NewReader(f)
 p := make([]byte, 12)
 index, _ := r.Read(p)
 fmt.Println(index)
 fmt.Println(string(p[:index]))
}

需要注意的是,p 字節切片的長度,一箇中文字符是 3 個字節,一個英文字符是 1 個字節。

ReadBytes 方法

func main() {
 f, _ := os.Open("/Users/frank/GolandProjects/go-package/lesson14/file.txt")
 defer f.Close()
 r := bufio.NewReader(f)
  bs, _ := r.ReadBytes('\n')
 fmt.Println(string(bs))
}

需要注意的是,分隔符參數是 byte 類型,使用單引號。

03 總結

本文我們以 bufio.Reader 爲例,介紹標準庫 bufio 的數據類型、初始化方式和提供的方法。

實際上標準庫 bufio 使用非常簡單,但是想要避免踩 “坑”,讀者朋友們最好是熟讀標準庫 bufio 的源碼 [2]。

參考資料

[1] 標準庫 bufio 的文檔: https://pkg.go.dev/bufio@go1.20.2

[2] 標準庫 bufio 的源碼: https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/bufio/

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/aP9bT4UL1MK5EDx18WfvmQ