内容简介:go标准库bufio.Scanner,从字面意思来看是一个扫描器、扫描仪。 所用是不停的从一个reader中读取数据兵缓存在内存中,还提供了一个注入函数用来自定义分割符。库中还提供了4个预定义分割方法。在看使用方法之前,我们需要先看一个函数。这个函数接受一个byte数组,和一个atEOF标志位(标志位用来表示是否还有更多的数据)返回的是3个返回值。第一个是推进输入的字节(一般为标志位字节数)
简介
go标准库bufio.Scanner,从字面意思来看是一个扫描器、扫描仪。 所用是不停的从一个reader中读取数据兵缓存在内存中,还提供了一个注入函数用来自定义分割符。库中还提供了4个预定义分割方法。
- ScanLines :以换行符分割('n')
- ScanWords :返回通过“空格”分词的单词
- ScanRunes :返回单个 UTF-8 编码的 rune 作为一个 token
- ScanBytes :返回单个字节作为一个 token
使用方法
在看使用方法之前,我们需要先看一个函数。
type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
这个函数接受一个byte数组,和一个atEOF标志位(标志位用来表示是否还有更多的数据)返回的是3个返回值。第一个是推进输入的字节(一般为标志位字节数)
func main() { input := "abcend234234234" fmt.Println(strings.Index(input,"end")) scanner := bufio.NewScanner(strings.NewReader(input)) scanner.Split(ScanEnd) //设置读取缓冲读取大小 每次读取2个字节 如果缓冲区不够则翻倍增加缓冲区大小 buf := make([]byte, 2) scanner.Buffer(buf, bufio.MaxScanTokenSize) for scanner.Scan() { fmt.Println("output:",scanner.Text()) } if scanner.Err() != nil { fmt.Printf("error: %s\n", scanner.Err()) } } func ScanEnd(data []byte, atEOF bool) (advance int, token []byte, err error) { //如果数据为空,数据已经读完直接返回 if atEOF && len(data) == 0 { return 0, nil, nil } // 获取自定义的结束标志位的位置 index:= strings.Index(string(data),"end") if index > 0{ //如果找到 返回的第一个参数为后推的字符长度 //第二个参数则指标志位之前的字符 //第三个参数为是否有错误 return index+3, data[0:index],nil } if atEOF { return len(data), data, nil } //如果没有找到则返回0,nil,nil return 0, nil, nil }
上面的例子可以看到 字符串是”abcend234234234“
因为设置的是每次读取2个字符串
第一次读取: buf = ab 没有找到end ScanEnd返回 0,nil,nil
第二次读取: buf = abce 没有找到end ScanEnd返回 0,nil,nil
第三次读取: buf = abcend23(buf翻倍扩容) 找到自定义标志位end 返回:6,abc, nil 打出 out abc
第四次读取: buf = 23423423 之前的已经读取的被去掉,犹豫buf大小为8 直接读取8个字符
第五次读取: 由于buf容量不足翻倍之后 直接获取全部数据输出 out 234234234
结果则是:
output: abc
output: 234234234
可以看到 扫描器 按照自定义的读取大小和结束符token 输出结果
源码查看
type Scanner struct { r io.Reader // reader split SplitFunc // 分割函数 又外部注入 maxTokenSize int // token最大长度 token []byte // split返回的最后一个令牌 buf []byte // 缓冲区字符 start int // buf中的第一个未处理字节 end int // buf中的数据结束 标志位 err error // Sticky error. empties int // 连续空令牌的计数 scanCalled bool // done bool // 扫描是否完成 } func (s *Scanner) Scan() bool { if s.done { return false } s.scanCalled = true // for循环知道找到token为止 for { if s.end > s.start || s.err != nil { // 调用split函数 得到返回值,函数中判断是否有token token往后推的标志位数 是否有错误 advance, token, err := s.split(s.buf[s.start:s.end], s.err != nil) if err != nil { if err == ErrFinalToken { s.token = token s.done = true return true } s.setErr(err) return false } if !s.advance(advance) { return false } s.token = token if token != nil { if s.err == nil || advance > 0 { s.empties = 0 } else { // Returning tokens not advancing input at EOF. s.empties++ if s.empties > 100 { panic("bufio.Scan: 100 empty tokens without progressing") } } return true } } //如果有错误 则返回false if s.err != nil { // Shut it down. s.start = 0 s.end = 0 return false } //重新设置开始位置 和结束位置 读取更多数据 if s.start > 0 && (s.end == len(s.buf) || s.start > len(s.buf)/2) { copy(s.buf, s.buf[s.start:s.end]) s.end -= s.start s.start = 0 } // 如果buf满了 如果满了重新创建一个长度为原来两倍大小的buf if s.end == len(s.buf) { const maxInt = int(^uint(0) >> 1) if len(s.buf) >= s.maxTokenSize || len(s.buf) > maxInt/2 { s.setErr(ErrTooLong) return false } newSize := len(s.buf) * 2 if newSize == 0 { newSize = startBufSize } if newSize > s.maxTokenSize { newSize = s.maxTokenSize } newBuf := make([]byte, newSize) copy(newBuf, s.buf[s.start:s.end]) s.buf = newBuf s.end -= s.start s.start = 0 } //如果没有找到则往后继续读取数据 for loop := 0; ; { n, err := s.r.Read(s.buf[s.end:len(s.buf)]) s.end += n if err != nil { s.setErr(err) break } if n > 0 { s.empties = 0 break } loop++ if loop > maxConsecutiveEmptyReads { s.setErr(io.ErrNoProgress) break } } } }
总结
根据上面的源码和例子可以看到这个扫描器的作用,当然正式使用时候不会只是读取一个写死的字符串。可以使用在读取scoket读取数据,IO 缓冲区 提供了一个临时存储区来存放数据,缓冲区存储的数据达到一定容量后才会被"释放"出来进行下一步存储,这种方式大大减少了写操作或是最终的系统调用被触发的次数,这无疑会在频繁使用系统资源的时候节省下巨大的系统开销。而对于读操作来说,缓冲 IO 意味着每次操作能够读取更多的数据,既减少了系统调用的次数,又通过以块为单位读取硬盘数据来更高效地使用底层硬件。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Spring事务源码分析专题(一)JdbcTemplate使用及源码分析
- 使用源码编译Hadoop
- GYHttpMock:使用及源码解析
- fishhook使用场景&源码分析
- WMRouter使用和源码分析
- 使用 Clion 阅读 Envoy 源码
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Mobilizing Web Sites
Layon, Kristofer / 2011-12 / 266.00元
Everyone has been talking about the mobile web in recent years, and more of us are browsing the web on smartphones and similar devices than ever before. But most of what we are viewing has not yet bee......一起来看看 《Mobilizing Web Sites》 这本书的介绍吧!