bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。

type Reader

Reader实现了给一个io.Reader接口对象附加缓冲。

func NewReader(rd io.Reader) Reader
NewReader创建一个具有默认大小缓冲、从r读取的
Reader。

func NewReaderSize(rd io.Reader, size int) Reader
NewReaderSize创建一个具有最少有size尺寸的缓冲、从r读取的
Reader。如果参数r已经是一个具有足够大缓冲的* Reader类型值,会返回r。

func (b *Reader) Reset(r io.Reader)
Reset丢弃缓冲中的数据,清除任何错误,将b重设为其下层从r读取数据。

func (b *Reader) Buffered() int
返回缓冲中现有的可读取的字节数。

func (b *Reader) Peek(n int) ([]byte, error)
Peek返回输入流的下n个字节,而不会移动读取位置。返回的[]byte只在下一次调用读取操作前合法。如果Peek返回的切片长度比n小,它也会返会一个错误说明原因。如果n比缓冲尺寸还大,返回的错误将是ErrBufferFull。

func (b *Reader) Read(p []byte) (n int, err error)
Read读取数据写入p。本方法返回写入p的字节数。本方法一次调用最多会调用下层Reader接口一次Read方法,因此返回值n可能小于len(p)。读取到达结尾时,返回值n将为0而err将为io.EOF。

  1. // 示例:Peek、Read、Discard、Buffered
  2. func main() {
  3. sr := strings.NewReader("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
  4. buf := bufio.NewReaderSize(sr, 0)
  5. b := make([]byte, 10)
  6. fmt.Println(buf.Buffered()) // 0
  7. s, _ := buf.Peek(5)
  8. s[0], s[1], s[2] = 'a', 'b', 'c'
  9. fmt.Printf("%d %q\n", buf.Buffered(), s) // 16 "abcDE"
  10. buf.Discard(1)
  11. //这个for与下面的for功能一样,但明显下面的for比较简洁
  12. for {
  13. n, err := buf.Read(b)
  14. if err!=nil{
  15. break
  16. }
  17. fmt.Printf("%d %q %v\n", buf.Buffered(), b[:n], err)
  18. }
  19. for n, err := 0, error(nil); err == nil; {
  20. n, err = buf.Read(b)
  21. fmt.Printf("%d %q %v\n", buf.Buffered(), b[:n], err)
  22. }
  23. //-------------------一次性读出----------------------------
  24. data := make([]byte, 0)
  25. for n, err := 0, error(nil); err == nil; {
  26. n, err = buf.Read(b)
  27. data = append(data,b[:n]...)
  28. }
  29. fmt.Printf("%q",data)
  30. }
  31. // 5 "bcDEFGHIJK" <nil>
  32. // 0 "LMNOP" <nil>
  33. // 6 "QRSTUVWXYZ" <nil>
  34. // 0 "123456" <nil>
  35. // 0 "7890" <nil>
  36. // 0 "" EOF
  37. //-------------------一次性读出----------------------------
  38. //ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890

func (b *Reader) ReadByte() (c byte, err error)
ReadByte读取并返回一个字节。如果没有可用的数据,会返回错误。

func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)
ReadLine是一个低水平的行数据读取原语。大多数调用者应使用ReadBytes(‘\n’)或ReadString(‘\n’)代替,或者使用Scanner。

func (b *Reader) ReadSlice(delim byte) (line []byte, err error)
ReadSlice读取直到第一次遇到delim字节,调用者应尽量使用ReadBytes或ReadString替代本法功法。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误

  1. // 示例:ReadSlice
  2. func main() {
  3. // 尾部有换行标记
  4. buf := bufio.NewReaderSize(strings.NewReader("ABCDEFG\n"), 0)
  5. for line, err := []byte{0}, error(nil); len(line) > 0 && err == nil; {
  6. line, err = buf.ReadSlice('\n')
  7. fmt.Printf("%q %v\n", line, err)
  8. }
  9. // "ABCDEFG\n" <nil>
  10. // "" EOF
  11. fmt.Println("----------")
  12. // 尾部没有换行标记
  13. buf = bufio.NewReaderSize(strings.NewReader("ABCDEFG"), 0)
  14. for line, err := []byte{0}, error(nil); len(line) > 0 && err == nil; {
  15. line, err = buf.ReadSlice('\n')
  16. fmt.Printf("%q %v\n", line, err)
  17. }
  18. // "ABCDEFG" EOF
  19. }


func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
ReadBytes读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的切片。如果ReadBytes方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误。

func (b *Reader) ReadString(delim byte) (line string, err error)
ReadString读取直到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的字符串。如果ReadString方法在读取到delim之前遇到了错误,它会返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadString方法返回的切片不以delim结尾时,会返回一个非nil的错误。

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

type Writer

Writer实现了为io.Writer接口对象提供缓冲。如果在向一个Writer类型值写入时遇到了错误,该对象将不再接受任何数据,且所有写操作都会返回该错误。在说有数据都写入后,调用者有义务调用Flush方法以保证所有的数据都交给了下层的io.Writer。

  1. w := bufio.NewWriter(os.Stdout)
  2. fmt.Fprint(w, "Hello, ")
  3. fmt.Fprint(w, "world!")
  4. w.Flush() // Don't forget to flush!
  5. //Hello, world!

func NewWriter(w io.Writer) Writer
创建一个具有默认大小缓冲、写入w的
Writer。

func NewWriterSize(w io.Writer, size int) Writer
NewWriterSize创建一个具有最少有size尺寸的缓冲、写入w的
Writer。如果参数w已经是一个具有足够大缓冲的*Writer类型值,会返回w。

func (b *Writer) Reset(w io.Writer)
Reset丢弃缓冲中的数据,清除任何错误,将b重设为将其输出写入w。

func (b *Writer) Buffered() int
Buffered返回缓冲中已使用的字节数。

func (b *Writer) Available() int
Available返回缓冲中还有多少字节未使用。

func (b *Writer) Write(p []byte) (nn int, err error)
Write将p的内容写入缓冲。返回写入的字节数。如果返回值nn < len(p),还会返回一个错误说明原因。

func (b *Writer) WriteString(s string) (int, error)
WriteString写入一个字符串。返回写入的字节数。如果返回值nn < len(s),还会返回一个错误说明原因。

func (b *Writer) WriteByte(c byte) error
WriteByte写入单个字节。

func (b *Writer) WriteRune(r rune) (size int, err error)
WriteRune写入一个unicode码值(的utf-8编码),返回写入的字节数和可能的错误。

func (b *Writer) Flush() error
Flush方法将缓冲中的数据写入下层的io.Writer接口。

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

  1. // 示例:Available、Buffered、WriteString、Flush
  2. func main() {
  3. buf := bufio.NewWriterSize(os.Stdout, 0)
  4. fmt.Println(buf.Available(), buf.Buffered()) // 4096 0
  5. buf.WriteString("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
  6. fmt.Println(buf.Available(), buf.Buffered()) // 4070 26
  7. // 缓存后统一输出,避免终端频繁刷新,影响速度
  8. buf.Flush() // ABCDEFGHIJKLMNOPQRSTUVWXYZ
  9. }

type ReadWriter

  1. type ReadWriter struct {
  2. *Reader
  3. *Writer
  4. }

ReadWriter类型保管了指向Reader和Writer类型的指针,(因此)实现了io.ReadWriter接口。

func NewReadWriter(r Reader, w Writer) *ReadWriter
NewReadWriter申请创建一个新的、将读写操作分派给r和w 的ReadWriter。

type Scanner

Scanner类型提供了方便的读取数据的接口,如从换行符分隔的文本里读取每一行。
成功调用的Scan方法会逐步提供文件的token,跳过token之间的字节。token由SplitFunc类型的分割函数指定;默认的分割函数会将输入分割为多个行,并去掉行尾的换行标志。本包预定义的分割函数可以将文件分割为行、字节、unicode码值、空白分隔的word。调用者可以定制自己的分割函数。
扫描会在抵达输入流结尾、遇到的第一个I/O错误、token过大不能保存进缓冲时,不可恢复的停止。当扫描停止后,当前读取位置可能会远在最后一个获得的token后面。需要更多对错误管理的控制或token很大,或必须从reader连续扫描的程序,应使用bufio.Reader代替。

func NewScanner(r io.Reader) *Scanner
NewScanner创建并返回一个从r读取数据的Scanner,默认的分割函数是ScanLines。

func (s *Scanner) Split(split SplitFunc)
Split设置该Scanner的分割函数。本方法必须在Scan之前调用。

func (s *Scanner) Scan() bool
Scan方法获取当前位置的token(该token可以通过Bytes或Text方法获得),并让Scanner的扫描位置移动到下一个token。当扫描因为抵达输入流结尾或者遇到错误而停止时,本方法会返回false。在Scan方法返回false后,Err方法将返回扫描时遇到的任何错误;除非是io.EOF,此时Err会返回nil。

func (s *Scanner) Bytes() []byte
Bytes方法返回最近一次Scan调用生成的token。底层数组指向的数据可能会被下一次Scan的调用重写。

func (s *Scanner) Text() string
Text方法返回最近一次Scan调用生成的token,会申请创建一个字符串保存token并返回该字符串。

func (s *Scanner) Err() error
Err返回Scanner遇到的第一个非EOF的错误。

type SplitFunc

type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
SplitFunc类型代表用于对输出作词法分析的分割函数。
参数data是尚未处理的数据的一个开始部分的切片,参数atEOF表示是否Reader接口不能提供更多的数据。返回值是解析位置前进的字节数,将要返回给调用者的token切片,以及可能遇到的错误。如果数据不足以(保证)生成一个完整的token,例如需要一整行数据但data里没有换行符,SplitFunc可以返回(0, nil, nil)来告诉Scanner读取更多的数据写入切片然后用从同一位置起始、长度更长的切片再试一次(调用SplitFunc类型函数)。
如果返回值err非nil,扫描将终止并将该错误返回给Scanner的调用者。
除非atEOF为真,永远不会使用空切片data调用SplitFunc类型函数。然而,如果atEOF为真,data却可能是非空的、且包含着未处理的文本。

func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
ScanBytes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每个字节作为一个token返回。

func ScanRunes(data []byte, atEOF bool) (advance int, token []byte, err error)
ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每个utf-8编码的unicode码值作为一个token返回。本函数返回的rune序列和range一个字符串的输出rune序列相同。错误的utf-8编码会翻译为U+FFFD = “\xef\xbf\xbd”,但只会消耗一个字节。调用者无法区分正确编码的rune和错误编码的rune。

func ScanWords(data []byte, atEOF bool) (advance int, token []byte, err error)
ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将空白(参见unicode.IsSpace)分隔的片段(去掉前后空白后)作为一个token返回。本函数永远不会返回空字符串。

func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error)
ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),本函数会将每一行文本去掉末尾的换行标记作为一个token返回。返回的行可以是空字符串。换行标记为一个可选的回车后跟一个必选的换行符。最后一行即使没有换行符也会作为一个token返回。

  1. // 示例:扫描
  2. func main() {
  3. // 逗号分隔的字符串,最后一项为空
  4. const input = "1,2,3,4,"
  5. scanner := bufio.NewScanner(strings.NewReader(input))
  6. // 定义匹配函数(查找逗号分隔的字符串)
  7. onComma := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
  8. for i := 0; i < len(data); i++ {
  9. if data[i] == ',' {
  10. return i + 1, data[:i], nil
  11. }
  12. }
  13. if atEOF {
  14. // 告诉 Scanner 扫描结束。
  15. return 0, data, bufio.ErrFinalToken
  16. } else {
  17. // 告诉 Scanner 没找到匹配项,让 Scan 填充缓存后再次扫描。
  18. return 0, nil, nil
  19. }
  20. }
  21. // 指定匹配函数
  22. scanner.Split(onComma)
  23. // 开始扫描
  24. for scanner.Scan() {
  25. fmt.Printf("%q ", scanner.Text())
  26. }
  27. // 检查是否因为遇到错误而结束
  28. if err := scanner.Err(); err != nil {
  29. fmt.Fprintln(os.Stderr, "reading input:", err)
  30. }
  31. }
  1. // 示例:带检查扫描
  2. func main() {
  3. const input = "1234 5678 1234567901234567890 90"
  4. scanner := bufio.NewScanner(strings.NewReader(input))
  5. // 自定义匹配函数
  6. split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
  7. // 获取一个单词
  8. advance, token, err = bufio.ScanWords(data, atEOF)
  9. // 判断其能否转换为整数,如果不能则返回错误
  10. if err == nil && token != nil {
  11. _, err = strconv.ParseInt(string(token), 10, 32)
  12. }
  13. // 这里包含了 return 0, nil, nil 的情况
  14. return
  15. }
  16. // 设置匹配函数
  17. scanner.Split(split)
  18. // 开始扫描
  19. for scanner.Scan() {
  20. fmt.Printf("%s\n", scanner.Text())
  21. }
  22. if err := scanner.Err(); err != nil {
  23. fmt.Printf("Invalid input: %s", err)
  24. }
  25. }