go
在提供了io
包的同时也提供了bufio
包来实现有缓存的读写操做以提升读写性能。为何bufio
性能比io
高呢?缓存
// 默认缓冲区大小 const ( defaultBufSize = 4096 ) // 最小缓冲区大小 自定义小于次阈值将会被覆盖 const minReadBufferSize = 16 // 使用默认缓冲区大小 bufio.NewReader(rd io.Reader) // 使用自定义缓冲区大小 bufio.NewReaderSize(rd io.Reader, size int)
缓冲读
的大体过程以下,设定好缓冲区大小buf_size
后,读取的字节数为rn
,缓冲的字节数为bn
:性能
rn >= buf_size
,则直接从文件读取,不启用缓冲。rn < buf_size
,则从文件读取buf_size
字节的内容到缓冲区,程序再从缓冲区中读取rn
字节的内容,此时缓冲区剩余bn = buf_size - rn
字节。rn < bn
,则从缓冲区读取rn
字节的内容,不发生文件IO
。rn >= bn
,则从缓冲区读取bn
字节的内容,不发生文件IO
,缓冲区置为空,回归1/2
步骤。缓冲读经过预读,能够在必定程度上减小文件IO
次数,故提升性能。code
代码演示:对象
package main import ( "bufio" "fmt" "strings" ) func main() { // 用 strings.Reader 模拟一个文件IO对象 strReader := strings.NewReader("12345678901234567890123456789012345678901234567890") // go 的缓冲区最小为 16 byte,咱们用最小值比较容易演示 bufReader := bufio.NewReaderSize(strReader, 16) // bn = 0 但 rn >= buf_size 缓冲区不启用 发生文件IO tmpStr := make([]byte, 16) n, _ := bufReader.Read(tmpStr) // bufReader buffered: 0, content: 1234567890123456 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 0 rn < buf_size 缓冲区启用 // 缓冲区从文件读取 buf_size 字节 发生文件IO // 程序从缓冲区读取 rn 字节 // 缓冲区剩余 bn = buf_size - rn 字节 tmpStr = make([]byte, 15) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 1, content: 789012345678901 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 1 rn > bn // 程序从缓冲区读取 bn 字节 缓冲区置空 不发生文件IO // 注意这里只能读到一个字节 tmpStr = make([]byte, 10) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 0, content: 2 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 0 rn < buf_size 启用缓冲读 发生文件IO // 缓冲区从文件读取 buf_size 字节 // 程序从缓冲区读取 rn 字节 // 缓冲区剩余 bn = buf_size - rn 字节 tmpStr = make([]byte, 10) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 6, content: 3456789012 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 6 rn <= bn // 则程序冲缓冲区读取 rn 字节 不发生文件IO tmpStr = make([]byte, 3) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 3, content: 345 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) // bn = 3 rn <= bn // 则程序冲缓冲区读取 rn 字节 不发生文件IO tmpStr = make([]byte, 3) n, _ = bufReader.Read(tmpStr) // bufReader buffered: 0, content: 678 fmt.Printf("bufReader buffered: %d, content: %s\n", bufReader.Buffered(), tmpStr[:n]) }
要注意的是当缓冲区中有内容时,程序的这次读取都会从缓冲区读,而不会发生文件IO。只有当缓冲区为空时,才会发生文件IO。若是缓冲区的大小足够,则启用缓冲读,先将内容载入填满缓冲区,程序再从缓冲区中读取。若是缓冲区太小,则会直接从文件读取,而不使用缓冲读。string
// 使用 defaultBufSize 大小 func NewWriter(w io.Writer) // 若是 size <= 0 则使用 defaultBufSize func NewWriterSize(w io.Writer, size int)
缓冲写
的大体过程以下,设定好缓冲区大小buf_size
后,写入的字节数为wn
,缓冲的字节数为bn
:it
wn >= buf_size
,则直接写入文件,不启用缓冲,发生文件IO。wn < buf_size
,则程序将内容写入缓冲区,不发生文件IO。wn + bn < buf_size
,则程序将内容写入缓冲区,不发生文件IO。wn + bn >= buf_size
,则缓冲区将buf_size
字节内容写入文件,缓冲区wn + bn - buf_size
的剩余内容。简单说就是要写入的内容先缓冲着,缓冲不下了则将缓冲区内容写入文件。io
代码演示:class
package main import ( "bufio" "fmt" "io" "strings" ) // 自定义一个 io.Writer 对象 type StringWriter struct { } func (s StringWriter) Write(p []byte) (n int, err error) { fmt.Printf("io write: %s\n", p) return len(p), nil } func main() { strReader := strings.NewReader("12345678901234567890") bufReader := bufio.NewReader(strReader) // 自定义的 io.Writer 对象 var stringWriter io.Writer stringWriter = StringWriter{} // 写缓冲大小为 6 为了更好的演示咱们自定义了一个 io.Writer bufWriter := bufio.NewWriterSize(stringWriter, 6) tmpStr := make([]byte, 8) for true { rn, err := bufReader.Read(tmpStr) if nil != err && io.EOF == err { break } _, err = bufWriter.Write(tmpStr[:rn]) fmt.Printf("\nread and write: %s\n", tmpStr[:rn]) fmt.Printf("bufWriter buffered: %d, available: %d, size: %d\n", bufWriter.Buffered(), bufWriter.Available(), bufWriter.Size()) fmt.Printf("----------------------\n") } // 缓冲区最后剩余的一些内容 _ = bufWriter.Flush() }
go run main.go // 没有发生写动做 '1234' 被缓冲 read and write: 1234 bufWriter buffered: 4, available: 2, size: 6 ---------------------- io write: 123456 // '12345678' 缓冲区满 则会写 '123456',继续缓冲 '78' read and write: 5678 bufWriter buffered: 2, available: 4, size: 6 ---------------------- // 没有发生写动做 '789012' 被缓冲 read and write: 9012 bufWriter buffered: 6, available: 0, size: 6 ---------------------- io write: 789012 // '7890123456' 缓冲区满 则会写 '789012',继续缓冲 '3456' read and write: 3456 bufWriter buffered: 4, available: 2, size: 6 ---------------------- io write: 345678 // '34567890' 缓冲区满 则会写 '345678',继续缓冲 '90' read and write: 7890 bufWriter buffered: 2, available: 4, size: 6 ---------------------- // 文件读取完毕,输出剩余的 '90' io write: 90