golang中bufio.SplitFunc的深入理解
在Golang实战开发的过程中,我们经常会遇到一些这样那样的问题,然后要卡好半天,等问题解决了才发现原来一些细节知识点还是没有掌握好。今天golang学习网就整理分享《golang中bufio.SplitFunc的深入理解》,聊聊包、bufio.splitfunc,希望可以帮助到正在努力赚钱的你。
前言
bufio模块是golang标准库中的模块之一,主要是实现了一个读写的缓存,用于对数据的读取或者写入操作。该模块在多个涉及io的标准库中被使用,比如http模块中使用buffio来完成网络数据的读写,压缩文件的zip模块利用bufio来操作文件数据的读写等。
golang的bufio包里面定以的SplitFunc是一个比较重要也比较难以理解的东西,本文希望通过结合简单的实例介绍SplitFunc的工作原理以及如何实现一个自己的SplitFunc。
一个例子
在bufio包里面定义了一些常用的工具比如Scanner,你可能需要读取用户在标准输入里面输入的一些东西,比如我们做一个复读机,读取用户的每一行输入,然后打印出来:
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
这个程序很简单,os.Stdin实现了io.Reader接口,我们从这个reader创建了一个scanner,设置分割函数为bufio.ScanLines,然后for循环,每次读到一行数据就将文本内容打印出来。麻雀虽小五脏俱全,这个小程序虽然简单,却引出了我们今天要介绍的对象: bufio.SplitFunc,它的定义是这个样子的:
package "buffio" type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
golang官方文档的描述是这个样子的:
SplitFunc is the signature of the split function used to tokenize the input. The arguments are an initial substring of the remaining unprocessed data and a flag, atEOF, that reports whether the Reader has no more data to give. The return values are the number of bytes to advance the input and the next token to return to the user, if any, plus an error, if any.
Scanning stops if the function returns an error, in which case some of the input may be discarded.
Otherwise, the Scanner advances the input. If the token is not nil, the Scanner returns it to the user. If the token is nil, the Scanner reads more data and continues scanning; if there is no more data--if atEOF was true--the Scanner returns. If the data does not yet hold a complete token, for instance if it has no newline while scanning lines, a SplitFunc can return (0, nil, nil) to signal the Scanner to read more data into the slice and try again with a longer slice starting at the same point in the input.
The function is never called with an empty data slice unless atEOF is true. If atEOF is true, however, data may be non-empty and, as always, holds unprocessed text.
英文!参数这么多!返回值这么多!好烦!不知道各位读者遇到这种文档会不会有这种感觉...正式由于这种情况,我才决定写一篇文章介绍一下SplitFunc的具体工作原理,用一种通俗的方式结合具体实例加以说明,希望对读者有所帮助。
好了,废话少说,开始正题吧!
Scanner和SplitFunc的工作机制
package "buffio" type SplitFunc func(data []byte, atEOF bool) (advance int, token []byte, err error)
Scanner是有缓存的,意思是Scanner底层维护了一个Slice用来保存已经从Reader中读取的数据,Scanner会调用我们设置SplitFunc,将缓冲区内容(data)和是否已经输入完了(atEOF)以参数的形式传递给SplitFunc,而SplitFunc的职责就是根据上述的两个参数返回下一次Scan需要前进几个字节(advance),分割出来的数据(token),以及错误(err)。
这是一个通信双向的过程,Scanner告诉我们的SplitFunc已经扫描到的数据和是否到结尾了,我们的SplitFunc则根据这些信息将分割的结果返回和下次扫描需要前进的位置返回给Scanner。用一个例子来说明:
package main
import (
"bufio"
"fmt"
"strings"
)
func main() {
input := "abcdefghijkl"
scanner := bufio.NewScanner(strings.NewReader(input))
split := func(data []byte, atEOF bool) (advance int, token []byte, err error) {
fmt.Printf("%t\t%d\t%s\n", atEOF, len(data), data)
return 0, nil, nil
}
scanner.Split(split)
buf := make([]byte, 2)
scanner.Buffer(buf, bufio.MaxScanTokenSize)
for scanner.Scan() {
fmt.Printf("%s\n", scanner.Text())
}
}
输出
false 2 ab
false 4 abcd
false 8 abcdefgh
false 12 abcdefghijkl
true 12 abcdefghijkl
这里我们把缓冲区的初始大小设置为了2,不够的时候会扩展为原来的2倍,最大为bufio.MaxScanTokenSize,这样一开始扫描2个字节,我们的缓冲区就满了,reader的内容还没有读取到EOF,然后split函数执行,输出:
false 2 ab
紧接着函数返回 0, nil, nil这个返回值告诉Scanner数据不够,下次读取的位置前进0位,需要继续从reader里面读取,此时因为缓冲区满了,所以容量扩展为2 * 2 = 4,reader的内容还没有读取到EOF,输出
false 4 abcd
重复上述步骤,一直到最后全部内容读取完了,EOF此时变成了true
true 12 abcdefghijkl
看了上面的过程是不是对SplitFunc的工作原来有了一点理解了呢?再回头看一下golang的官方文档有没有觉得稍微理解了一点?下面是bufio.ScanLines的实现,读者可以自己研究一下该函数是如何工作的
标准库里的ScanLines
func ScanLines(data []byte, atEOF bool) (advance int, token []byte, err error) {
// 表示我们已经扫描到结尾了
if atEOF && len(data) == 0 {
return 0, nil, nil
}
// 找到\n的位置
if i := bytes.IndexByte(data, '\n'); i >= 0 {
// 把下次开始读取的位置向前移动i + 1位
return i + 1, dropCR(data[0:i]), nil
}
// 这里处理的reader内容全部读取完了,但是内容不为空,所以需要把剩余的数据返回
if atEOF {
return len(data), dropCR(data), nil
}
// 表示现在不能分割,向Reader请求更多的数据
return 0, nil, nil
}
参考
In-depth introduction to bufio.Scanner in Golang
总结
今天关于《golang中bufio.SplitFunc的深入理解》的内容介绍就到此结束,如果有什么疑问或者建议,可以在golang学习网公众号下多多回复交流;文中若有不正之处,也希望回复留言以告知!
golang复用http.request.body的方法示例
- 上一篇
- golang复用http.request.body的方法示例
- 下一篇
- Golang如何调用Python代码详解
-
- 美好的帆布鞋
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢大佬分享技术贴!
- 2023-02-05 09:26:25
-
- 外向的面包
- 这篇文章内容出现的刚刚好,太详细了,赞 ??,已收藏,关注老哥了!希望老哥能多写Golang相关的文章。
- 2023-01-31 22:50:04
-
- 隐形的小虾米
- 这篇文章太及时了,太详细了,真优秀,已加入收藏夹了,关注博主了!希望博主能多写Golang相关的文章。
- 2023-01-28 22:28:32
-
- 冷傲的书本
- 这篇文章真是及时雨啊,很详细,受益颇多,已收藏,关注作者了!希望作者能多写Golang相关的文章。
- 2022-12-31 10:16:45
-
- 爱撒娇的吐司
- 很棒,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,帮助很大,总算是懂了,感谢楼主分享文章!
- 2022-12-25 11:26:27
-
- Golang · Go教程 | 1小时前 | 错误处理 · regexp · Go教程 · 配置热加载 · Go regexp.Compile 正则表达式配置 配置错误处理
- Go regexp 编译正则失败时怎么把错误交给配置层
- 222浏览 收藏
-
- Golang · Go教程 | 2小时前 |
- Go regexp.FindAllStringSubmatchIndex 怎么映射捕获组位置
- 134浏览 收藏
-
- Golang · Go教程 | 2小时前 | go · 有理数 · 高精度计算 · 浮点误差 · math/big.Rat FloatString big.NewRat SetFrac64 SetString RatString
- Go math/big.Rat 怎么精确计算分数而不是浮点近似
- 312浏览 收藏
-
- Golang · Go教程 | 3小时前 |
- Go strconv.NumError 怎么拿到具体转换字段
- 301浏览 收藏
-
- Golang · Go教程 | 3小时前 |
- Go strconv.ParseFloat 遇到 NaN 和 Inf 怎么区分
- 501浏览 收藏
-
- Golang · Go教程 | 5小时前 | Timer · go · 性能优化 · time.After ·
- Go time.After 在高频循环中为什么会造成延迟对象堆积
- 313浏览 收藏
-
- Golang · Go教程 | 5小时前 |
- Go strconv.AppendQuote 怎么低分配地拼接带引号字符串
- 184浏览 收藏
-
- Golang · Go教程 | 6小时前 | 并发 · 定时器 · go · time.Timer time.NewTimer time.Stop time.Reset
- Go time.Timer 通道读不到事件时怎么判断定时器当前状态
- 173浏览 收藏
-
- Golang · Go教程 | 6小时前 | 定时任务 · 并发控制 · Go教程 · 资源释放 · Go Goroutine context 后台任务 time.Ticker Ticker.Stop
- Go time.Ticker 不再使用时怎么停止避免后台任务残留
- 297浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 52次使用
-
- SuperCLUE
- SuperCLUE是权威的中文大语言模型综合评测基准,涵盖语言理解、知识应用、AI Agent智能体及安全性等12项核心能力。通过多轮对话与客观测试,定期发布榜单与技术报告,为模型研发、优化及行业选型提供科学依据。
- 202次使用
-
- C-Eval
- 深入了解C-Eval中文评估套件,涵盖52个学科与4级难度。本文详解其功能特点、Zero-shot/Few-shot使用方法及代码示例,助您全面评测LLM中文理解与泛化能力。
- 139次使用
-
- AI Prompt Library
- 探索AI Prompt Library免费资源库,涵盖营销、写作及多场景AI提示词。兼容ChatGPT、Claude等工具,一键复制优化输出,提升工作效率。
- 69次使用
-
- Generrated
- Generrated汇集9300+张DALL·E生成图像及对应提示词,支持查看完整图集、对比DALL·E 2与3版本差异,是AI绘图新手学习Prompt设计与获取创作灵感的实用工具。
- 51次使用
-
- Go time包AddDate使用解惑实例详解
- 2023-01-11 164浏览
-
- golang包循环引用的几种解决方案总结
- 2023-01-07 208浏览
-
- Golang template 包基本原理分析
- 2022-12-31 500浏览
-
- Go语言包和包管理详解
- 2023-02-24 334浏览
-
- GO中sync包自由控制并发示例详解
- 2023-01-07 391浏览
