带缓冲的IO

bufio.Scanner

Scanner为读取数据提供了一些方便的接口,通过切割方法可以逐字节,逐字,逐词组,逐行读取,某些场景下使用scanner可以很方便的解决问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func main() {
// 通过NewSacnner生成一个实例对象
scanner := bufio.NewScanner(os.Stdin)

// 循环输入内容
for {
fmt.Print("请输入内容:")
// 扫描输入的内容,如果没有扫描到数据则返回false
scanner.Scan()

// 通过判断进行跳出循环,当输入的内容是quit时跳出循环
if "quit" == scanner.Text() {
break
}
// scanner.Text获取输入的内容
fmt.Println("你输入的内容是:", scanner.Text())
}
}

运行示例

1
2
3
4
5
6
7
请输入内容:a b c
你输入的内容是: a b c
请输入内容:123
你输入的内容是: 123
请输入内容:quit

进程完成,并显示退出代码 0

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

bufio.Readder

缓冲读的大致过程如下,设定好缓冲区大小buf_size后,读取的字节数为rn,缓冲的字节数为bn

  1. 如果缓冲区为空,且 rn >= buf_size,则直接从文件读取,不启用缓冲。
  2. 如果缓冲区为空,且 rn < buf_size,则从文件读取buf_size 字节的内容到缓冲区,程序再从缓冲区中读取rn字节的内容,此时缓冲区剩余bn = buf_size - rn字节。
  3. 如果缓冲区不为空,rn < bn,则从缓冲区读取rn字节的内容,不发生文件IO
  4. 如果缓冲区不为空,rn >= bn,则从缓冲区读取bn字节的内容,不发生文件IO,缓冲区置为空,回归1/2步骤。

缓冲读通过预读,可以在一定程度上减少文件IO次数,故提高性能

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
func main() {
// user.txt文件内容
// 1,笔记,0,2021-08-04 20:55,张三,false
// 2,整理,0,2021-08-04 20:56,李四,false
file, _ := os.Open("user.txt")
defer file.Close()

reader := bufio.NewReader(file)

//// 定义一个字节切片,读取内容保存至字节切片中
//read := make([]byte, 10)
//reader.Read(read)
//fmt.Println(string(read)) // 1,笔记,0
//reader.ReadByte()

//// 当读取指定字节时停止读取并返回内容
//ctx,_ := reader.ReadBytes(',')
//fmt.Println(string(ctx)) // 1,

//// 通过换行符读取一行内容
//ctx,_ := reader.ReadBytes('\n')
//fmt.Println(string(ctx)) // 1,笔记,0,2021-08-04 20:55,张三,false

//// 也可以使用 ReadLine 读取一行内容
//ctx, prefix, _ := reader.ReadLine()
//fmt.Println(string(ctx),prefix) // 1,笔记,0,2021-08-04 20:55,张三,false false
//// 也可以使用 ReadString 读取一行内容
//ctx,_ := reader.ReadString('\n')
//fmt.Println(string(ctx)) // 1,笔记,0,2021-08-04 20:55,张三,false

// 将流输出到标准输出
reader.WriteTo(os.Stdout)
}

要注意的是当缓冲区中有内容时,程序的此次读取都会从缓冲区读,而不会发生文件IO。只有当缓冲区为空时,才会发生文件IO。如果缓冲区的大小足够,则启用缓冲读,先将内容载入填满缓冲区,程序再从缓冲区中读取。如果缓冲区过小,则会直接从文件读取,而不使用缓冲读。

bufio.Writer

缓冲写的大致过程如下,设定好缓冲区大小buf_size后,写入的字节数为wn,缓冲的字节数为bn

  1. 如果缓冲区为空,且 wn >= buf_size,则直接写入文件,不启用缓冲,发生文件IO。
  2. 如果缓冲区为空,且 wn < buf_size,则程序将内容写入缓冲区,不发生文件IO。
  3. 如果缓冲区不为空,wn + bn < buf_size,则程序将内容写入缓冲区,不发生文件IO。
  4. 如果缓冲区不为空,wn + bn >= buf_size,则缓冲区将buf_size字节内容写入文件,缓冲区wn + bn - buf_size的剩余内容。

简单说就是要写入的内容先缓冲着,缓冲不下了则将缓冲区内容写入文件。

代码示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
func main() {
// 创建文件并打开
file,_ := os.Create("test.txt")
defer file.Close()

// 创建带缓冲的写对象
writer := bufio.NewWriter(file)
// 延迟刷新缓存内容,不然运行后看不到写入文件内容
defer writer.Flush()

// 将缓存的数据写入到文件中
writer.Write([]byte("abcdef\n"))
writer.WriteString("我爱Go语言")
}