bufio模塊通過對io模塊的封裝,提供了數據緩沖功能,能夠一定程度減少大塊數據讀寫帶來的開銷。實際上在bufio各個組件內部都維護了一個緩沖區,數據讀寫操作都直接通過緩存區進行。當發起一次讀寫操作時,會首先嘗試從緩沖區獲取數據;只有當緩沖區沒有數據時,才會從數據源獲取數據更新緩沖。
Reader
可以通過NewReader函數創建bufio.Reader對象,函數接收一個io.Reader作為參數;也就是說,bufio.Reader不能直接使用,需要綁定到某個io.Reader上。函數聲明如下:
func NewReader(rd io.Reader) *Reader
func NewReaderSize(rd io.Reader, size int) *Reader // 可以配置緩沖區的大小
相較於io.Reader,bufio.Reader提供了很多實用的方法,能夠更有效的對數據進行讀取。首先是幾個基礎方法,它們能夠對Reader進行細粒度的操作:
Read,讀取n個byte數據
Discard,丟棄接下來n個byte數據
Peek,獲取當前緩沖區內接下來的n個byte,但是不移動指針
Reset,清空整個緩沖區
具體的方法聲明如下:
func (b *Reader) Read(p []byte) (n int, err error)
func (b *Reader) Discard(n int) (discarded int, err error)
func (b *Reader) Peek(n int) ([]byte, error)
func (b *Reader) Reset(r io.Reader)
除了上面的基礎操作之外,bufio.Reader還提供了多個更高抽象層次的方法對數據進行簡單的結構化讀取。主要包括如下幾個方法:
ReadByte,讀取一個byte
ReadRune,讀取一個utf-8字符
ReadLine,讀取一行數據,由’\n’分隔
ReadBytes,讀取一個byte列表
ReadString,讀取一個字符串
其中前三個函數都沒有參數,會從緩沖區讀取一個滿足需求的數據。后面兩個函數接收一個參數delim,用於做數據拆分,持續讀取數據直到當前字節的值等於delim,然后返回這些數據;實際上這兩個函數功能相同,只是在函數返回值的類型上有所區別。具體的方法聲明如下:
func (b *Reader) ReadByte() (byte, error)
func (b *Reader) ReadRune() (r rune, size int, err error)
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
func (b *Reader) ReadBytes(delim byte) ([]byte, error)
func (b *Reader) ReadString(delim byte) (string, error)
下面是一個簡單的示例,使用ReadString方法獲取用‘ ’分隔的字符串。
package main import ( "bufio" "fmt" "strings" ) func main() { r := strings.NewReader("hello world !") reader := bufio.NewReader(r) for { str, err := reader.ReadString(byte(' ')) fmt.Println(str) if err != nil { return } } }
Scanner
實際使用中,更推薦使用Scanner對數據進行讀取,而非直接使用Reader類。Scanner可以通過splitFunc將輸入數據拆分為多個token,然后依次進行讀取。
和Reader類似,Scanner需要綁定到某個io.Reader上,通過NewScannner進行創建,函數聲明如下:
func NewScanner(r io.Reader) *Scanner
在使用之前還需要設置splitFunc(默認為ScanLines),splitFunc用於將輸入數據拆分為多個token。bufio模塊提供了幾個默認splitFunc,能夠滿足大部分場景的需求,包括:
ScanBytes,按照byte進行拆分
ScanLines,按照行(“\n”)進行拆分
ScanRunes,按照utf-8字符進行拆分
ScanWords,按照單詞(” “)進行拆分
通過Scanner的Split方法,可以為Scanner指定splitFunc。使用方法如下:
scanner := bufio.NewScanner(os.StdIn)
scanner.split(bufio.ScanWords)
除此了默認的splitFunc之外,也可以定義自己的splitFunc,函數需要滿足如下聲明:
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
函數接收兩個參數,第一個參數是輸入數據,第二個參數是一個標識位,用於標識當前數據是否為結束。函數返回三個參數,第一個是本次split操作的指針偏移;第二個是當前讀取到的token;第三個是返回的錯誤信息。
在完成了Scanner初始化之后,通過Scan方法可以在輸入中向前讀取一個token,讀取成功返回True;使用Text和Bytes方法獲取這個token,Text返回一個字符串,Bytes返回字節數組。方法聲明如下:
func (s *Scanner) Scan() bool
func (s *Scanner) Text() string
func (s *Scanner) Text() []byte
下面的示例使用Scanner對上面的示例進行了重現,可以看到和Reader相比,Scanner的使用更加便捷。
package main import ( "bufio" "strings" "fmt" ) func main() { scanner := bufio.NewScanner(strings.NewReader("hello world !")) scanner.Split(bufio.ScanWords) for scanner.Scan() { fmt.Println(scanner.Text()) } }
Writer
和Reader類似,Writer也對應的提供了多組方法。基礎方法包括如下幾個:
func (b *Writer) Write(p []byte) (nn int, err error) // 寫入n byte數據
func (b *Writer) Reset(w io.Writer) // 重置當前緩沖區
func (b *Writer) Flush() error // 清空當前緩沖區,將數據寫入輸出
此外,Writer也提供了多個方法方便我們進行數據寫入操作:
func (b *Writer) WriteByte(c byte) error // 寫入一個字節
func (b *Writer) WriteRune(r rune) (size int, err error) // 寫入一個字符
func (b *Writer) WriteString(s string) (int, error) // 寫入一個字符串