![e8f1aded46b55585fe55c64fa4693f56.png](https://img-blog.csdnimg.cn/img_convert/e8f1aded46b55585fe55c64fa4693f56.png)
众所周知,带缓冲的 IO 标准库 一直是 Go 中优化读写操作的利器。对于写操作来说,在被发送到 socket
或硬盘之前,IO 缓冲区
提供了一个临时存储区来存放数据,缓冲区存储的数据达到一定容量后才会被"释放"出来进行下一步存储,这种方式大大减少了写操作或是最终的系统调用被触发的次数,这无疑会在频繁使用系统资源的时候节省下巨大的系统开销。而对于读操作来说,缓冲 IO
意味着每次操作能够读取更多的数据,既减少了系统调用的次数,又通过以块为单位读取硬盘数据来更高效地使用底层硬件。本文会更加侧重于讲解 bufio 包中的 Scanner 扫描器模块,它的主要作用是把数据流分割成一个个标记并除去它们之间的空格。
"foo bar baz"
如果我们只想得到上面字符串中的单词,那么扫描器能帮我们按顺序检索出 "foo","bar" 和 "baz" 这三个单词( 查看源码 )
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())
}
}
输出结果:
foo
bar
baz
Scanner
扫描器读取数据流的时候会使用带缓冲区的 IO,并接受 io.Reader
作为参数。
如果你需要在内存中处理字符串或者是 bytes 切片,可以首先考虑使用 bytes.Split 或是 strings.Split 这样的工具集,当处理这些流数据时,bytes
或是 strings
标准库中的方法可能是最简单可靠的。
在底层,扫描器使用缓冲不断存储数据,当缓冲区非空或者是读到文件的末尾时 (EOF) split
函数会被调用,目前我们介绍了一个预定义好的 split
函数,但根据下面的函数签名来看,它的用途可能更加广泛。
func(data []byte, atEOF bool) (advance int, token []byte, err error)
目前为止,我们知道 Split
函数会在读数据的时候被调用,从返回值来看,它的执行应该有 3 种不同情况。
1. 需要补充更多的数据
这表示传入的数据还不足以生成一个字符流的标记,当返回的值分别是 0, nil, nil
的时候,扫描器会尝试读取更多的数据,如果缓冲区已满,那么缓冲区会在任何读取操作前自动扩容为原来的两倍,让我们来仔细看一下这个过程 查看源码
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
input := "abcdefghijkl"
scanner := bufio.NewScanner(strings.NewReader