标准库bufio个人详解
本文是我有通俗的语言写的如果有误请指出。
先看bufio官方文档
https://studygolang.com/pkgdoc文档地址
主要分三部分Reader、Writer、Scanner
分别是读数据、写数据和扫描器三种数据类型的相关操作 这个扫描后面会详细说我开始也没弄明白其实很简单。
Reader
func NewReaderSize
func NewReaderSize(rd io.Reader, size int) *Reader
NewReaderSize创建一个具有最少有size尺寸的缓冲、从r读取的*Reader。如果参数r已经是一个具有足够大缓冲的* Reader类型值,会返回r。
解释:看官方解释这个方法可能不太容易懂,这个意思就是就是你可以给*Reader自定义一个size大小的缓冲区,*Reader每次从底层io.Reader(也就是你那个文件或者流)中预读size大小的数据到缓冲区中(可能读不满),然后你每次读数据实际是从这个缓冲区中拿数据。
下面是NewReaderSize源码
func NewReaderSize(rd io.Reader, size int) *Reader { // Is it already a Reader? b, ok := rd.(*Reader) if ok && len(b.buf) >= size { return b } if size < minReadBufferSize { //minReadBufferSize==16 size = minReadBufferSize } r := new(Reader) r.reset(make([]byte, size), rd) return r }
r.reset 初始化了一个*Reader 返回大小是size。
func NewReader
func NewReader(rd io.Reader) *Reader
NewReader创建一个具有默认大小缓冲、从r读取的*Reader。
解释:那这个NewReader就很好解释了 和NewReaderSize基本一样就是缓冲区大小是默认设置好的
func (*Reader) Peek
func (b *Reader) Peek(n int) ([]byte, error)
解释:Peek就是返回缓存的一个切片,该切片引用缓存中的前N个字节的数据,如果n大于总大小,则返回能读到的字节数的数据。
func (*Reader) Read
func (b *Reader) Read(p []byte) (n int, err error)
Read读取数据写入p。本方法返回写入p的字节数。本方法一次调用最多会调用下层Reader接口一次Read方法,因此返回值n可能小于len(p)。读取到达结尾时,返回值n将为0而err将为io.EOF。
解释:如果缓存不为空则直接从缓存中读数据不会从底层io.Reader读,如果缓存为空len(p)>缓存大小,则直接从底层io.Reader读数据到p。
如果len(p)<缓存大小,则先从底层io.Reader中读数据到缓存再到p。
主要就这几个 还有几个文档写的都很清楚易懂我就不多写了。
Writer类型的方法和Reader类型的方法差不多也很易懂主要就一个Flush要注意。
func (*Writer) Flush
func (b *Writer) Flush() error
Flush方法将缓冲中的数据写入下层的io.Writer接口。
和Reader是倒过来的,Writer每次写数据是先写入缓冲区的,进程缓冲区填满后,通过进程缓冲写入到内核缓冲再写入到磁盘,使用Flush就不等填满直接走写入流程了,保证你的数据及时写入文件。
解释:scanner类型扫描器 官方的说法很复杂,我也没太看懂找了很多资料,其实就是你在数据传输的时候时候使用“分隔符”,scanner类型可以通过分隔符逐个迭代你的数据。
上面4个函数func Scan…… 就是分隔符的判断函数这4个是给你预设好的,你也可以按照自己的需求改写。
怎么改写呢,看下面
func (*Scanner) Split
func (s *Scanner) Split(split SplitFunc)
这个Split方法就是设置你这个scanner的用哪个SplitFunc类型的函数
在看下面这个SpliFunc类型的函数签名
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
照着这个格式写一个不就得了么,当然具体写法给出了但是你不会?没关系咱看一下官方是咋写的。
https://github.com/golang/go/blob/master/src/bufio/scan.go?name=release#57官方源码地址
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) { if atEOF && len(data) == 0 { return 0, nil, nil } if i := bytes.IndexByte(data, '\n'); i >= 0 { // We have a full newline-terminated line. return i + 1, dropCR(data[0:i]), nil } // If we're at EOF, we have a final, non-terminated line. Return it. if atEOF { return len(data), dropCR(data), nil } // Request more data. return 0, nil, nil }
看bytes.IndexByte(data, ‘\n’);这段不就是在找行尾嘛 比如你想改成以“;”为分隔符的就改成bytes.IndexByte(data, ‘;’);不就得了么
func main(){ scanner:=bufio.NewScanner( strings.NewReader("abcdefg\nhigklmn"), ) scanner.Split(ScanLines) //这里可以随意选择用哪个函数也可以自定义,可以不指定默认为\n做分隔符
for scanner.Scan(){
fmt.Println(scanner.Text())
}
}
到此为止拉~