- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章golang中bufio.SplitFunc的深入理解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前言 。
bufio模块是golang标准库中的模块之一,主要是实现了一个读写的缓存,用于对数据的读取或者写入操作。该模块在多个涉及io的标准库中被使用,比如http模块中使用buffio来完成网络数据的读写,压缩文件的zip模块利用bufio来操作文件数据的读写等.
golang的bufio包里面定以的SplitFunc是一个比较重要也比较难以理解的东西,本文希望通过结合简单的实例介绍SplitFunc的工作原理以及如何实现一个自己的SplitFunc.
一个例子 。
在bufio包里面定义了一些常用的工具比如Scanner,你可能需要读取用户在标准输入里面输入的一些东西,比如我们做一个复读机,读取用户的每一行输入,然后打印出来:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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,它的定义是这个样子的:
1
2
|
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的工作机制 。
1
2
|
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。用一个例子来说明:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
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 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
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 。
总结 。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我的支持.
原文链接:https://www.jianshu.com/p/9121ca1df9fb 。
最后此篇关于golang中bufio.SplitFunc的深入理解的文章就讲到这里了,如果你想了解更多关于golang中bufio.SplitFunc的深入理解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我有这样的代码 scanner := bufio.NewScanner(reader) scanner.Split(splitJSON) for scanner.Scan() { bb :=
我是一名优秀的程序员,十分优秀!