golang中bufio包的用法


原文链接: golang中bufio包的用法

使用bufio处理TCP粘包问题
Go按行读取数据的坑 · Issue #10 · ma6174/blog
判断是否是空白字符: unicode.IsSpace()
bufio.Reader 结构体中所有读取数据的方法,都包含了delim分隔符,这个用起来很不方便,所以Google对此在go1.1版本中加入了

bufio.Scanner结构体,用于读取数据

type Scanner struct {

r            io.Reader // The reader provided by the client.
split        SplitFunc // The function to split the tokens.   
maxTokenSize int       // Maximum size of a token; modified by tests.
token        []byte    // Last token returned by split.
buf          []byte    // Buffer used as argument to split.
start        int       // First non-processed byte in buf.
end          int       // End of data in buf.
err          error     // Sticky error.
empties      int       // Count of successive empty tokens.
scanCalled   bool      // Scan has been called; buffer is in use.
done         bool      // Scan has finished.

}

  1. 默认扫描方式按行扫描 ScanLines.
  2. 单次扫描文本最大值是64k MaxScanTokenSize = 64 * 1024

    按行读取数据三种方式

  3. 除非能确定行长度不超过65536,否则不要使用bufio.Scanner!如果建议使用ReadBytes或ReadString

  4. 当然也可以在调用Scan()之前设置buffer和MaxScanTokenSize的大小 比如: scanner.Buffer([]byte{}, bufio.MaxScanTokenSize*10)

  5. 按行去读取数据在 bufio.Reader 中也提供了 ReadLine()ReadString('\n') 可供选择

1. 使用 ReadLine()

特殊情况需要注意的就是: 当数据末尾没有\n的时候,直到EOF还没有分隔符\n,这时候返回EOF错误,但是line里面还是有数据的,如果不处理的话就会漏掉最后一行.

    s := "a\nb\nc"
    reader := bufio.NewReader(strings.NewReader(s))
    for {
        line, err := reader.ReadString('\n')
        if err != nil {
            if err == io.EOF {
                // **处理当数据末尾没有\n的情况**
                fmt.Printf("%#v\n", line)
                break
            }
            panic(err)
        }
        fmt.Printf("%#v\n", line)
    }
    // 关键点:最后一次返回的错误是 io.EOF,line 里面是可能有数据的。这样不会遗漏最后一行,
    buf := bufio.NewReader(bytes.NewReader(r))
  		for {
  			line, err := buf.ReadString('\n')
  			if err == nil || err == io.EOF {
  				line = strings.TrimSpace(line)
  				if line != "" {
  					fmt.Println(line)
  				}
  			}
  			if err != nil {
  				break
  			}
  		}

2. 使用 scanner.Scan()

d


// Split functions defaults to ScanLines

// ScanBytes is a split function for a Scanner that returns each byte as a token.
// data []byte 待处理扫描的数据
// atEOF bool  判断数据是否处理完成

// advance int  返回要处理的字节数
// token []byte 保存要处理的内容
// 
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error) {
	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}
	return 1, data[0:1], nil
}

func main(){
    scanner:=bufio.NewScanner(strings.NewReader("ABCDEFG\nHIJKELM"),)
    // 可以在调用Scan之前设置buffer和MaxScanTokenSize的大小
    scanner.Buffer([]byte{}, bufio.MaxScanTokenSize*10)
    for scanner.Scan(){
        fmt.Println(scanner.Text()) // scanner.Bytes()
    }
//  通过 scanner.Err(); 我们可以捕捉到 扫描中的错误信息,这对单行文件超过 MaxScanTokenSize 时特别有用
    if err := scanner.Err(); err != nil {
        fmt.Fprintln(os.Stderr, "reading standard input:", err)
    }
}

其他扫描方式

package main
import (
    "bufio"
    "fmt"
    "strings"
)
func main() {
    input := "foo   bar      baz"
    scanner := bufio.NewScanner(strings.NewReader(input))
    scanner.Split(bufio.ScanWords)
    for scanner.Scan() {
        fmt.Println(scanner.Text())
    }
}

理解: 对于Scanner.Scan()方法,相当于其他语言的迭代器iterator,并把迭代器指向的数据存放到新的缓冲区里。

  新的缓冲区(token)可以通过scanner.Text()或者scanner.Bytes()获取到。

Scanner.Scan 方法默认是以换行符\n,作为分隔符。如果你想指定分隔符,Go语言提供了四种形式:

ScanBytes(返回单个字节作为一个 token),
ScanLines(返回一行文本), 
ScanRunes(返回单个 UTF-8 编码的 rune 作为一个 token)
ScanWords(返回通过“空格”分词的单词)。 

使用方式:

func main(){
    scanner:=bufio.NewScanner(strings.NewReader("ABCDEFG\nHIJKELM"))
    scanner.Split(ScanWords)/*四种方式之一,你也可以自定义, 实现SplitFunc方法*/
    for scanner.Scan(){
        fmt.Println(scanner.Text()) // scanner.Bytes()
    }
}
func ParseInput(input string) (int, int) {
    f, err := os.Open(filePath)
	defer fh.Close()
	if err != nil {
		return nil, err
	}

	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		line := scanner.Text()
		items := strings.SplitN(line, delimiter, 2)
		key := strings.TrimSpace(items[0])
		value := strings.TrimSpace(items[1])
		settings[key] = value
}
}

结论:我们一般在读取数据到缓冲区时,且想要采用分隔符分隔数据流时,我们一般使用bufio.Scanner数据结构,而不使用bufio.Reader。

bufio是golang标准库,表示缓冲IO的意思,可读可写。通过bufio标准库,实现对输入数据的读取、处理和输出操作

我们可以由此了解并使用bufio库中常用的方法:

bufio实现了io.Reader和io.Writer接口, 所以可读可写。

bufio.Reader作用是把oldReader的数据读取到缓冲区,它的常用方法列表

reader:=bufio.NewReader(oldReader) // 默认size为4096
reader:=bufio.NewReaderSize(oldReader, size)

bytes, error = reader.ReadSlice(delim) /*结束符*/
bytes, error = reader.ReadString(delim)
bytes, error = reader.ReadLine()        // 默认换行符\n
bytes, error = reader.ReadBytes(delim)
n, error = reader.Reader([]byte)

var bts = make([]byte, 100) // 初始化,保证有足够写入空间
reader:=bufio.NewReader(strings.NewReader("ABCDEFG\nHIJKLMN\n")
_, err = reader.Read(bts) //传递写入副本

这里要平时注意的一点:var bts = make([]byte, 100), 需要初始化。
slice类型变量首先要初始化, 如果不初始化,则如果在调用的方法体内初始化,则返回时,slice类型变量还是旧值。原因说明:

slice底层实际上是struct结构体类型,有len,cap和指针数组组成。所以函数体内初始化则内存地址和传入的内存地址不是一致的,读到的数据是旧的
如果传入参数是初始化的,则相当于func(temp=Struct{}/传入参数param/), 虽然地址不一样,但是重新赋值了,且len、cap值相同,底层的指针数组还是指向一样的内存地址,如果底层数组的内存地址存放的数据发生变化,由于指向数组的指针没有变化,则传入参数可是指向了底层数组新的数据。

结论:所以凡是遇到slice类型变量,作为参数传入时,都需要进行make初始化,且指定slice的长度len。

bufio标准库中的Reader和Writer,最好用于文件IO操作,把数据先缓存到内存中,然后再整体做文件IO操作,尽最大可能地减少磁盘IO,但是内存缓冲区的大小要合理设置,默认大小是4096个字节。

bufio.Writer使用

bufio标准库中的Writer提供的方法列表

type Writer struct {
    err error
    buf []byte
    n   int
    wr  io.Writer
}

// 实例化bufio.Writer, 实例化是会直接分配大小为len(w.buf)大小的内存空间,Writer.n表示内存缓冲区已经存放的字节大小
func NewWriter(w io.Writer) *Writer
func NewWriterSize(w io.Writer, size int) *Writer

// 表示可用的内存缓冲区大小len(b.buf)-n
func (b *Writer) Avaliable() int

// 表示已使用的内存缓冲区大小b.n
func (b *Writer) Buffered() int

// 这个首字母大写,表示用户可以手动触发内存缓冲区的数据,回写到wr.Write所指定的地方,一般为磁盘IO回写
func (b *Writer) Flush() error

// bufio.Writer把数据写到缓冲区挺有意思的。
// 开发者可以阅读源码了解一下。我举第二个方法Write([]byte)
func (b *Writer) ReadFrom(r io.Reader) (int64, error)
func (b *Writer) Write(p []byte) (int, error)
func (b *Writer) WriteByte(c byte) error
func (b *Writer) WriteRune(r rune) (int, error)
func (b *Writer) WriteString(s string) (int, error)

对于func (b *Writer) Write(p []byte) (int, error)方法的正确理解:

如果内存缓冲区剩余空间小于len(p),分两种情况讨论:
    如果当前内存缓冲区为空,则直接把p数据写入到磁盘IO,b.wr.Write(p);
    如果当前内存缓冲区不空,则首先把缓冲区填满,然后先把内存缓冲区的数据进行一次磁盘IO回写操作,之后内存缓冲区可用大小有是len(Writer.buf)长度了,这时又分两种情况讨论:第一种:如果剩余要处理的p数据小于内存缓冲区的大小, 则把剩余数据p写入到内存缓冲区;第二种:如果剩余要处理的p数据大于等于内存缓冲区,则没必要缓冲了,直接整体一次回写到磁盘。
如果内存缓冲区剩余空间大于等于len(p), 则先把数据暂存到缓冲区,减少磁盘IO。

总结:bufio的Reader和Writer操作一般适用于磁盘IO的读写场景。同时了解标准库的实现, 可以提高自己的编程思维。标准库bufio确实写得很有意思。

bufio 包中的函数和方法
// bufio 包实现了带缓存的 I/O 操作
// 它封装一个 io.Reader 或 io.Writer 对象
// 使其具有缓存和一些文本读写功能


type Reader struct {
            buf          []byte
            rd           io.Reader // reader provided by the client
            r, w         int       // buf read and write positions
            err          error
            lastByte     int
            lastRuneSize int
        }

// NewReaderSize 将 rd 封装成一个拥有 size 大小缓存的 bufio.Reader 对象
// 如果 rd 的基类型就是 bufio.Reader 类型,而且拥有足够的缓存
// 则直接将  rd 转换为基类型并返回
func NewReaderSize(rd io.Reader, size int) *Reader
// NewReader 相当于 NewReaderSize(rd, 4096)
func NewReader(rd io.Reader) *Reader

// Peek 返回缓存的一个切片,该切片引用缓存中前 n 字节数据
// 该操作不会将数据读出,只是引用
// 引用的数据在下一次读取操作之前是有效的
// 如果引用的数据长度小于 n,则返回一个错误信息
// 如果 n 大于缓存的总大小,则返回 ErrBufferFull
// 通过 Peek 的返回值,可以修改缓存中的数据
// 但是不能修改底层 io.Reader 中的数据

func main() {
s := strings.NewReader("ABCDEFG")
br := bufio.NewReader(s)

b, _ := br.Peek(5)
fmt.Printf("%s\n", b)
// ABCDE

b[0] = 'a'
b, _ = br.Peek(5)
fmt.Printf("%s\n", b)
// aBCDE
}

// Read 从 b 中读出数据到 p 中,返回读出的字节数
// 如果 p 的大小 >= 缓存的总大小,而且缓存不为空
// 则只能读出缓存中的数据,不会从底层 io.Reader 中提取数据
// 如果 p 的大小 >= 缓存的总大小,而且缓存为空
// 则直接从底层 io.Reader 向 p 中读出数据,不经过缓存
// 只有当 b 中无可读数据时,才返回 (0, io.EOF)
func (b *Reader) Read(p \[\]byte) (n int, err error)

func main() {
s := strings.NewReader("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
br := bufio.NewReader(s)
b := make([]byte, 20)

n, err := br.Read(b)
fmt.Printf("%-20s %-2v %v\n", b[:n], n, err)
// ABCDEFGHIJKLMNOPQRST 20 <nil>

n, err = br.Read(b)
fmt.Printf("%-20s %-2v %v\n", b[:n], n, err)
// UVWXYZ1234567890 16 <nil>

n, err = br.Read(b)
fmt.Printf("%-20s %-2v %v\n", b[:n], n, err)
// 0 EOF
}

// ReadByte 从 b 中读出一个字节并返回
// 如果 b 中无可读数据,则返回一个错误
func (b *Reader) ReadByte() (c byte, err error)
// UnreadByte 撤消最后一次读出的字节
// 只有最后读出的字节可以被撤消
// 无论任何操作,只要有内容被读出,就可以用 UnreadByte 撤消一个字节
func (b *Reader) UnreadByte() error

func main() {
s := strings.NewReader("ABCDEFG")
br := bufio.NewReader(s)

c, _ := br.ReadByte()
fmt.Printf("%c\n", c)
// A

c, _ = br.ReadByte()
fmt.Printf("%c\n", c)
// B

br.UnreadByte()
c, _ = br.ReadByte()
fmt.Printf("%c\n", c)
// B
}

// ReadRune 从 b 中读出一个 UTF8 编码的字符并返回
// 同时返回该字符的 UTF8 编码长度
// 如果 UTF8 序列无法解码出一个正确的 Unicode 字符
// 则只读出 b 中的一个字节,并返回 U+FFFD 字符,size 返回 1
func (b *Reader) ReadRune() (r rune, size int, err error)
// UnreadRune 撤消最后一次读出的 Unicode 字符
// 如果最后一次执行的不是 ReadRune 操作,则返回一个错误
// 因此,UnreadRune 比 UnreadByte 更严格
func (b *Reader) UnreadRune() error

func main() {
s := strings.NewReader("你好,世界!")
br := bufio.NewReader(s)

c, size, _ := br.ReadRune()
fmt.Printf("%c %v\n", c, size)
// 你 3

c, size, _ = br.ReadRune()
fmt.Printf("%c %v\n", c, size)
// 好 3

br.UnreadRune()
c, size, _ = br.ReadRune()
fmt.Printf("%c %v\n", c, size)
// 好 3
}

// Buffered 返回缓存中数据的长度

func (b *Reader) Buffered() int

func main() {
s := strings.NewReader("你好,世界!")
br := bufio.NewReader(s)

fmt.Println(br.Buffered())
// 0

br.Peek(1)
fmt.Println(br.Buffered())
// 18
}

// ReadSlice 在 b 中查找 delim 并返回 delim 及其之前的所有数据的切片
// 该操作会读出数据,返回的切片是已读出数据的引用
// 切片中的数据在下一次读取操作之前是有效的
//
// 如果 ReadSlice 在找到 delim 之前遇到错误
// 则读出缓存中的所有数据并返回,同时返回遇到的错误(通常是 io.EOF)
// 如果在整个缓存中都找不到 delim,则 err 返回 ErrBufferFull
// 如果 ReadSlice 能找到 delim,则 err 始终返回 nil
//
// 因为返回的切片中的数据有可能被下一次读写操作修改
// 因此大多数操作应该使用 ReadBytes 或 ReadString,它们返回的不是数据引用
func (b *Reader) ReadSlice(delim byte) (line \[\]byte, err error)

func main() {
s := strings.NewReader("ABC DEF GHI JKL")
br := bufio.NewReader(s)

w, _ := br.ReadSlice(' ')
fmt.Printf("%q\n", w)
// "ABC "

w, _ = br.ReadSlice(' ')
fmt.Printf("%q\n", w)
// "DEF "

w, _ = br.ReadSlice(' ')
fmt.Printf("%q\n", w)
// "GHI "
}

// ReadLine 是一个低级的原始的行读取操作
// 大多数情况下,应该使用 ReadBytes('\\n') 或 ReadString('\\n')
// 或者使用一个 Scanner
//
// ReadLine 通过调用 ReadSlice 方法实现,返回的也是缓存的切片
// ReadLine 尝试返回一个单行数据,不包括行尾标记(\\n 或 \\r\\n)
// 如果在缓存中找不到行尾标记,则设置 isPrefix 为 true,表示查找未完成
// 同时读出缓存中的数据并作为切片返回
// 只有在当前缓存中找到行尾标记,才将 isPrefix 设置为 false,表示查找完成
// 可以多次调用 ReadLine 来读出一行
// 返回的数据在下一次读取操作之前是有效的
// 如果 ReadLine 无法获取任何数据,则返回一个错误信息(通常是 io.EOF)
func (b *Reader) ReadLine() (line \[\]byte, isPrefix bool, err error)

func main() {
s := strings.NewReader("ABC\nDEF\r\nGHI\r\nJKL")
br := bufio.NewReader(s)

w, isPrefix, _ := br.ReadLine()
fmt.Printf("%q %v\n", w, isPrefix)
// "ABC" false

w, isPrefix, _ = br.ReadLine()
fmt.Printf("%q %v\n", w, isPrefix)
// "DEF" false

w, isPrefix, _ = br.ReadLine()
fmt.Printf("%q %v\n", w, isPrefix)
// "GHI" false
}

// ReadBytes 在 b 中查找 delim 并读出 delim 及其之前的所有数据
// 如果 ReadBytes 在找到 delim 之前遇到错误
// 则返回遇到错误之前的所有数据,同时返回遇到的错误(通常是 io.EOF)
// 只有当 ReadBytes 找不到 delim 时,err 才不为 nil
// 对于简单的用途,使用 Scanner 可能更方便
func (b *Reader) ReadBytes(delim byte) (line \[\]byte, err error)

func main() {
s := strings.NewReader("ABC DEF GHI JKL")
br := bufio.NewReader(s)

w, _ := br.ReadBytes(' ')
fmt.Printf("%q\n", w)
// "ABC "

w, _ = br.ReadBytes(' ')
fmt.Printf("%q\n", w)
// "DEF "

w, _ = br.ReadBytes(' ')
fmt.Printf("%q\n", w)
// "GHI "
}

// ReadString 功能同 ReadBytes,只不过返回的是一个字符串
func (b *Reader) ReadString(delim byte) (line string, err error)

func main() {
s := strings.NewReader("ABC DEF GHI JKL")
br := bufio.NewReader(s)

w, _ := br.ReadString(' ')
fmt.Printf("%q\n", w)
// "ABC "

w, _ = br.ReadString(' ')
fmt.Printf("%q\n", w)
// "DEF "

w, _ = br.ReadString(' ')
fmt.Printf("%q\n", w)
// "GHI "
}

// WriteTo 实现了 io.WriterTo 接口
func (b *Reader) WriteTo(w io.Writer) (n int64, err error)

func main() {
s := strings.NewReader("ABCEFG")
br := bufio.NewReader(s)
b := bytes.NewBuffer(make([]byte, 0))

br.WriteTo(b)
fmt.Printf("%s\n", b)
// ABCEFG
}


  `  func (b *Reader) Reset(r io.Reader)`

reset丢弃任何的缓存数据,丛植所有状态并且将缓存读切换到r


package main

import (
    "bufio"
    "fmt"
    "strings"
)

func main() {
    s := strings.NewReader("ABCEFG")
    str := strings.NewReader("123455")
    br := bufio.NewReader(s)
    b, _ := br.ReadString('\n')
    fmt.Println(b)     //ABCEFG
    br.Reset(str)
    b, _ = br.ReadString('\n')
    fmt.Println(b)     //123455
}



<span style="font-size:18px">// Writer 实现了带缓存的 io.Writer 对象
// 如果在向 Writer 中写入数据的过程中遇到错误
// 则 Writer 不会再接受任何数据
// 而且后续的写入操作都将返回错误信息
type Writer struct {


       err   error
       buf  []byte
       n     int
       wr   io.Writer

}
// NewWriterSize 将 wr 封装成一个拥有 size 大小缓存的 bufio.Writer 对象
// 如果 wr 的基类型就是 bufio.Writer 类型,而且拥有足够的缓存
// 则直接将 wr 转换为基类型并返回
func NewWriterSize(wr io.Writer, size int) *Writer
// NewWriter 相当于 NewWriterSize(wr, 4096)
func NewWriter(wr io.Writer) *Writer

// Flush 将缓存中的数据提交到底层的 io.Writer 中
func (b *Writer) Flush() error
// Available 返回缓存中的可以空间
func (b *Writer) Available() int
// Buffered 返回缓存中未提交的数据长度
func (b *Writer) Buffered() int
// Write 将 p 中的数据写入 b 中,返回写入的字节数
// 如果写入的字节数小于 p 的长度,则返回一个错误信息
func (b *Writer) Write(p \[\]byte) (nn int, err error)
// WriteString 同 Write,只不过写入的是字符串
func (b *Writer) WriteString(s string) (int, error)

func main() {
    b := bytes.NewBuffer(make([]byte, 0))
    bw := bufio.NewWriter(b)
    fmt.Println(bw.Available()) // 4096
    fmt.Println(bw.Buffered())  // 0

    bw.WriteString("ABCDEFGH")
    fmt.Println(bw.Available()) // 4088
    fmt.Println(bw.Buffered())  // 8
    fmt.Printf("%q\n", b)       // ""

    bw.Flush()
    fmt.Println(bw.Available()) // 4096
    fmt.Println(bw.Buffered())  // 0
    fmt.Printf("%q\n", b)       // "ABCEFG"
}

// WriteByte 向 b 中写入一个字节
func (b *Writer) WriteByte(c byte) error
// WriteRune 向 b 中写入 r 的 UTF8 编码
// 返回 r 的编码长度
func (b *Writer) WriteRune(r rune) (size int, err error)

func main() {
    b := bytes.NewBuffer(make([]byte, 0))
    bw := bufio.NewWriter(b)
    bw.WriteByte('H')
    bw.WriteByte('e')
    bw.WriteByte('l')
    bw.WriteByte('l')
    bw.WriteByte('o')
    bw.WriteByte(' ')
    bw.WriteRune('世')
    bw.WriteRune('界')
    bw.WriteRune('!')
    bw.Flush()
    fmt.Println(b) // Hello 世界!

}

// ReadFrom 实现了 io.ReaderFrom 接口
func (b *Writer) ReadFrom(r io.Reader) (n int64, err error)

func main() {
b := bytes.NewBuffer(make([]byte, 0))
s := strings.NewReader("Hello 世界!")
bw := bufio.NewWriter(b)
bw.ReadFrom(s)
//bw.Flush()            //ReadFrom无需使用Flush,其自己已经写入.
fmt.Println(b) // Hello 世界!
}


    func (b *Writer) Reset(w io.Writer)

Reset丢弃任何没有写入的缓存数据,清除任何错误并且重新将b指定它的输出结果指向w


package main

import (
    "bufio"
    "bytes"
    "fmt"
)

func main() {
    b := bytes.NewBuffer(make([]byte, 0))
    bw := bufio.NewWriter(b)
    bw.WriteString("123")
    c := bytes.NewBuffer(make([]byte, 0))
    bw.Reset(c)
    bw.WriteString("456")
    bw.Flush()
    fmt.Println(b)       //输出为空
    fmt.Println(c)  //输出456
}

// ReadWriter 集成了 bufio.Reader 和 bufio.Writer
// 它实现了 io.ReadWriter 接口


type ReadWriter struct {
*Reader
*Writer
}

// NewReadWriter 封装 r 和 w 为一个 bufio.ReadWriter 对象
func NewReadWriter(r *Reader, w *Writer) *ReadWriter


package main

import (
    "bufio"
    "bytes"
    "fmt"
    "strings"
)

func main() {
    b := bytes.NewBuffer(make([]byte, 0))
    bw := bufio.NewWriter(b)
    s := strings.NewReader("123")
    br := bufio.NewReader(s)
    rw := bufio.NewReadWriter(br, bw)
    p, _ := rw.ReadString('\n')
    fmt.Println(string(p))              //123
    rw.WriteString("asdf")
    rw.Flush()
    fmt.Println(b)                          //asdf
}

// scan.go
// Scanner 提供了一个方便的接口来读取数据,例如读取一个多行文本
// 连续调用 Scan 方法将扫描数据中的“指定部分”,跳过各个“指定部分”之间的数据
// Scanner 使用了缓存,所以“指定部分”的长度不能超出缓存的长度
// Scanner 需要一个 SplitFunc 类型的“切分函数”来确定“指定部分”的格式
// 本包中提供的“切分函数”有<span style="color:#FF0000">“行切分函数”、“字节切分函数”、“UTF8字符编码切分函数”
// 和“单词切分函数”,用户也可以自定义“切分函数”
// 默认的“切分函数”为“行切分函数”,用于获取数据中的一行数据(不包括行尾符)
//
// 扫描在遇到下面的情况时会停止:
// 1、数据扫描完毕,遇到 io.EOF
// 2、遇到读写错误
// 3、“指定部分”的长度超过了缓存的长度
// 如果要对数据进行更多的控制,比如的错误处理或扫描更大的“指定部分”或顺序扫描
// 则应该使用 bufio.Reader


    type Scanner struct {
        r            io.Reader // The reader provided by the client.
        split        SplitFunc // The function to split the tokens.
        maxTokenSize int       // Maximum size of a token; modified by tests.
        token        []byte    // Last token returned by split.
        buf          []byte    // Buffer used as argument to split.
        start        int       // First non-processed byte in buf.
        end          int       // End of data in buf.
        err          error     // Sticky error.
    }

// SplitFunc 用来定义“切分函数”类型
// data 是要扫描的数据
// atEOF 标记底层 io.Reader 中的数据是否已经读完
// advance 返回 data 中已处理的数据长度
// token 返回找到的“指定部分”
// err 返回错误信息
// 如果在 data 中无法找到一个完整的“指定部分”
// 则 SplitFunc 返回 (0, nil) 来告诉 Scanner
// 向缓存中填充更多数据,然后再次扫描
//
// 如果返回的 err 是非 nil 值,扫描将被终止,并返回错误信息
//
// 如果 data 为空,则“切分函数”将不被调用
// 意思是在 SplitFunc 中不必考虑 data 为空的情况
//
// SplitFunc 的作用很简单,从 data 中找出你感兴趣的数据,然后返回
// 并告诉调用者,data 中有多少数据你已经处理过了
type SplitFunc func(data \[\]byte, atEOF bool) (advance int, token \[\]byte, err error)
// NewScanner 创建一个 Scanner 来扫描 r
// 默认切分函数为 ScanLines
func NewScanner(r io.Reader) *Scanner
// Err 返回扫描过程中遇到的非 EOF 错误
// 供用户调用,以便获取错误信息
func (s *Scanner) Err() error

// Bytes 将最后一次扫描出的“指定部分”作为一个切片返回(引用传递)
// 下一次的 Scan 操作会覆盖本次返回的结果
func (s *Scanner) Bytes() \[\]byte
// Text 将最后一次扫描出的“指定部分”作为字符串返回(值传递)
func (s *Scanner) Text() string

// Scan 在 Scanner 的数据中扫描“指定部分”
// 找到后,用户可以通过 Bytes 或 Text 方法来取出“指定部分”
// 如果扫描过程中遇到错误,则终止扫描,并返回 false
func (s *Scanner) Scan() bool

func main() {
s := strings.NewReader("ABC\nDEF\r\nGHI\nJKL")
bs := bufio.NewScanner(s)
for bs.Scan() {
fmt.Printf("%s %v\n", bs.Bytes(), bs.Text())
}
// ABC ABC
// DEF DEF
// GHI GHI
// JKL JKL
}

// Split 用于设置 Scanner 的“切分函数”
// 这个函数必须在调用 Scan 前执行
func (s *Scanner) Split(split SplitFunc)

func main() {
s := strings.NewReader("ABC DEF GHI JKL")
bs := bufio.NewScanner(s)
bs.Split(bufio.ScanWords)
for bs.Scan() {
fmt.Println(bs.Text())
}
// ABC
// DEF
// GHI
// JKL
}

// ScanBytes 是一个“切分函数”
// 用来找出 data 中的单个字节并返回
func ScanBytes(data \[\]byte, atEOF bool) (advance int, token \[\]byte, err error)

func main() {
s := strings.NewReader("Hello 世界!")
bs := bufio.NewScanner(s)
bs.Split(bufio.ScanBytes)
for bs.Scan() {
fmt.Printf("%s ", bs.Text())
}
}

// ScanRunes 是一个“切分函数”
// 用来找出 data 中的单个 UTF8 字符的编码并返回
// 如果 UTF8 解码出错,则返回的 U+FFFD 会被做为 "\\xef\\xbf\\xbd" 返回
// 这使得用户无法区分“真正的U+FFFD字符”和“解码错误的返回值”
func ScanRunes(data \[\]byte, atEOF bool) (advance int, token \[\]byte, err error)

func main() {
s := strings.NewReader("Hello 世界!")
bs := bufio.NewScanner(s)
bs.Split(bufio.ScanRunes)
for bs.Scan() {
fmt.Printf("%s ", bs.Text())
} // H e l l o 世 界 !
}

// ScanLines 是一个“切分函数”
// 用来找出 data 中的单行数据并返回(包括空行)
// 行尾标记可能是 \\n 或 \\r\\n(返回值不包括行尾标记)
func ScanLines(data \[\]byte, atEOF bool) (advance int, token \[\]byte, err error)

// ScanWords 是一个“切分函数”
// 用来找出 data 中的单词
// 单词以空白字符分隔,空白字符由 unicode.IsSpace 定义
func ScanWords(data \[\]byte, atEOF bool) (advance int, token \[\]byte, err error)
`