1. 程式人生 > >標準庫bufio個人詳解

標準庫bufio個人詳解

本文是我有通俗的語言寫的如果有誤請指出。

先看bufio官方文件

https://studygolang.com/pkgdoc文件地址

 

 主要分三部分Reader、Writer、Scanner

分別是讀資料、寫資料和掃描器三種資料型別的相關操作 這個掃描後面會詳細說我開始也沒弄明白其實很簡單。


 

Reader

func NewReaderSize

func NewReaderSize(rd io.Reader, size int) *Reader

NewReaderSize建立一個具有最少有size尺寸的緩衝、從r讀取的*Reader。如果引數r已經是一個具有足夠大緩衝的* Reader型別值,會返回r。

 

 

 解釋:看官方解釋這個方法可能不太容易懂,這個意思就是就是你可以給*Reader自定義一個size大小的緩衝區,*Reader每次從底層io.Reader(也就是你那個檔案或者流)中預讀size大小的資料到緩衝區中(可能讀不滿),然後你每次讀資料實際是從這個緩衝區中拿資料。

 

 下面是NewReaderSize原始碼

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 { //minReadBufferSize==16
        size = minReadBufferSize
    }
    r := new(Reader)
    r.reset(make([]byte, size), rd)
    return r
}

  r.reset 初始化了一個*Reader 返回大小是size。

func NewReader

func NewReader(rd io.Reader) *Reader

NewReader建立一個具有預設大小緩衝、從r讀取的*Reader。

解釋:那這個NewReader就很好解釋了 和NewReaderSize基本一樣就是緩衝區大小是預設設定好的

func (*Reader) Peek

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

解釋:Peek就是返回快取的一個切片,該切片引用快取中的前N個位元組的資料,如果n大於總大小,則返回能讀到的位元組數的資料。

func (*Reader) Read

func (b *Reader) Read(p []byte) (n int, err error)

Read讀取資料寫入p。本方法返回寫入p的位元組數。本方法一次呼叫最多會呼叫下層Reader介面一次Read方法,因此返回值n可能小於len(p)。讀取到達結尾時,返回值n將為0而err將為io.EOF。

解釋:如果快取不為空則直接從快取中讀資料不會從底層io.Reader讀,如果快取為空len(p)>快取大小,則直接從底層io.Reader讀資料到p。

如果len(p)<快取大小,則先從底層io.Reader中讀資料到快取再到p。

 

主要就這幾個 還有幾個文件寫的都很清楚易懂我就不多寫了。

Writer型別的方法和Reader型別的方法差不多也很易懂主要就一個Flush要注意。

func (*Writer) Flush

func (b *Writer) Flush() error

Flush方法將緩衝中的資料寫入下層的io.Writer介面。

和Reader是倒過來的,Writer每次寫資料是先寫入緩衝區的,程序緩衝區填滿後,通過程序緩衝寫入到核心緩衝再寫入到磁碟,使用Flush就不等填滿直接走寫入流程了,保證你的資料及時寫入檔案。

 


 

 

 解釋:scanner型別掃描器 官方的說法很複雜,我也沒太看懂找了很多資料,其實就是你在資料傳輸的時候時候使用“分隔符”,scanner型別可以通過分隔符逐個迭代你的資料。

上面4個函式func Scan……  就是分隔符的判斷函式這4個是給你預設好的,你也可以按照自己的需求改寫。

怎麼改寫呢,看下面

func (*Scanner) Split

func (s *Scanner) Split(split SplitFunc)

這個Split方法就是設定你這個scanner的用哪個SplitFunc型別的函式

在看下面這個SpliFunc型別的函式簽名

type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)

照著這個格式寫一個不就得了麼,當然具體寫法給出了但是你不會?沒關係咱看一下官方是咋寫的。

https://github.com/golang/go/blob/master/src/bufio/scan.go?name=release#57官方原始碼地址

func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	if i := bytes.IndexByte(data, '\n'); i >= 0 {
		// We have a full newline-terminated line.
		return i + 1, dropCR(data[0:i]), nil
	}
	// If we're at EOF, we have a final, non-terminated line. Return it.
	if atEOF {
		return len(data), dropCR(data), nil
	}
	// Request more data.
	return 0, nil, nil
}

   

看bytes.IndexByte(data, '\n');這段不就是在找行尾嘛 比如你想改成以“;”為分隔符的就改成bytes.IndexByte(data, ';');不就得了麼

func main(){
    scanner:=bufio.NewScanner(
        strings.NewReader("abcdefg\nhigklmn"),
    )
    scanner.Split(ScanLines) //這裡可以隨意選擇用哪個函式也可以自定義,可以不指定預設為\n做分隔符
  for scanner.Scan(){
    fmt.Println(scanner.Text())
  }
}

  

到此為止拉~

 

 

 

&n