本文是我有通俗的语言写的如果有误请指出.
先看 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())
- }
- }
到此为止拉~
来源: http://www.bubuko.com/infodetail-3304115.html