Go 学习之 io.Reader 从源码了解如何编写

参考

Reader和Writer 接口

接口的定义

type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

作用

Reader 接口

  • 实现该接口的【类型(数据结构)】,可以理解为【可供数据读出的数据容器】,调用【Read方法】,即可实现从【数据容器中读取数据】

Writer 接口

  • 实现该接口的【类型(数据结构)】,可以理解为【可供数据写入的数据容器】,调用【Write方法】,即可实现从【向数据容器中写入数据】

Reader 接口 和 Writer 接口

  • 同时实现这两个接口的【类型(数据结构)】,可以理解为【可供数据写入和读出的数据容器】

常用的几个实现

围绕io.Reader/Writer,有几个常用的实现:

  • net.Conn, os.Stdin, os.File: 网络、标准输入输出、文件的流读取
  • strings.Reader: 把字符串抽象成Reader
  • bytes.Reader: 把[]byte抽象成Reader
  • bytes.Buffer: 把[]byte抽象成Reader和Writer
  • bufio.Reader/Writer: 抽象成带缓冲的流读取(比如按行读写)

如何实现这两个接口

Read(p []byte) (n int, err error)

  • 负责将数据读取,写入到 p []byte 中

Write(p []byte) (n int, err error)

  • 负责 从 p []byte 中读取数据,写到需要的地方
type Reader interface {
    Read(p []byte) (n int, err error)
}

type Writer interface {
    Write(p []byte) (n int, err error)
}

// 如何实现这两个方法
// Read 就是将数据读取,写入到 p []byte 中
Read(p []byte) (n int, err error)

// 示例
// /usr/local/Cellar/go@1.17/1.17.8/libexec/src/strings/reader.go
// Read implements the io.Reader interface.
func (r *Reader) Read(b []byte) (n int, err error) {
	if r.i >= int64(len(r.s)) {
		return 0, io.EOF
	}
	r.prevRune = -1
  // r.i 记录了当前数据读到了 哪个位置
  // n 为拷贝数据的长度 
  // copy 第一个参数为 需要填充的, 第二个参数为 被拷贝的数据
  // 若第一个参数空间 < 第二个参数长度,那么只拷贝第一个参数能容纳的长度
  // 如 b 为 []byte 2长度 ,r.s[r.i:]=“adsgsg”
  // 那么执行 copy 后,b 为“ad"
	n = copy(b, r.s[r.i:])
	r.i += int64(n)
	return
}

// Write 就是从 p []byte 中读取数据,写到需要的地方
Write(p []byte) (n int, err error)

// 示例
// /usr/local/Cellar/go@1.17/1.17.8/libexec/src/strings/builder.go
// Write appends the contents of p to b's buffer.
// Write always returns len(p), nil.
func (b *Builder) Write(p []byte) (int, error) {
	b.copyCheck()
	b.buf = append(b.buf, p...)
	return len(p), nil
}

代码详解

代码示例

// testReadstring
func main() {
	s := strings.NewReader("亲爱的提奥,梵高")
	br := bufio.NewReader(s)
	// ReadString 在 br 中查找 delim 并返回 delim 及其之前的所有数据。
	// func (b *Reader) ReadString(delim byte) (line []byte, err error)

	n, err := br.ReadString(',')
	fmt.Printf("%s  %v\n", n, err) // 亲爱的提奥,  <nil>

	// 如果未找到 delim 且遇到错误(通常是 io.EOF),则返回缓存中的所
	n, err = br.ReadString(',')
	fmt.Printf("%s  %v\n", n, err) // 梵高  EOF
	fmt.Println(n)
	fmt.Println(string(n))

}
Q1 | 为什么不直接使用 bufio.NewReader

可以看到,bufio.NewReader 传入的参数需要实现了io.Reader接口,因此直接传入普通文本"亲爱的提奥,梵高" 不匹配 io.Reader接口,会显示数据类型不匹配,无法识别

// /usr/local/Cellar/go@1.17/1.17.8/libexec/src/bufio/bufio.go

// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) *Reader {
	return NewReaderSize(rd, defaultBufSize)
}

// Reader implements buffering for an io.Reader object.
type Reader struct {
	buf          []byte
	rd           io.Reader // reader provided by the client
	r, w         int       // buf read and write positions
	err          error
	lastByte     int // last byte read for UnreadByte; -1 means invalid
	lastRuneSize int // size of last rune read for UnreadRune; -1 means invalid
}


因此,将普通文本"亲爱的提奥,梵高"通过strings.NewReader函数,转换为实现了 io.Reader接口的*strings.Reader结构,之后便可传入了bufio.NewReader函数,从而构建*bufio.Reader结构

  • 问题?为什么非得要采用 *bufio.Reader结构
    • 因为bufio包,具有的功能更多(如 换行读取,Scan 函数,可指定截断函数(如br.Split(bufio.ScanWords),按字阶段,读取)的Scan函数,以及上面示例中指定截断符号的读取一行的函数( br.ReadString(‘,’)))
      • 同时 bufio 还具有 Write函数,可将从底层读取的,重新写入到别处
    • strings 包可能只具有简单的一些读取函数
// /usr/local/Cellar/go@1.17/1.17.8/libexec/src/strings/reader.go

// NewReader returns a new Reader reading from s.
// It is similar to bytes.NewBufferString but more efficient and read-only.
func NewReader(s string) *Reader { return &Reader{s, 0, -1} }

// A Reader implements the io.Reader, io.ReaderAt, io.ByteReader, io.ByteScanner,
// io.RuneReader, io.RuneScanner, io.Seeker, and io.WriterTo interfaces by reading
// from a string.
// The zero value for Reader operates like a Reader of an empty string.
type Reader struct {
	s        string
	i        int64 // current reading index
	prevRune int   // index of previous rune; or < 0
}

分析函数调用逻辑

s := strings.NewReader("亲爱的提奥,梵高")
br := bufio.NewReader(s)

n, err := br.ReadString(',')
1 | 初始化创建 bufio.NewReader
  1. 首先s := strings.NewReader("亲爱的提奥,梵高")创建了 *strings.Reader 结构,并进行了初始化,记录了原始文本亲爱的提奥,梵高
  2. 之后 br := bufio.NewReader(s) 通过传入的*strings.Reader 结构,创建了*strings.Reader结构并进行了初始化,通过指针rd记录了引用的*strings.Reader 结构
  • strings.NewReader(“亲爱的提奥,梵高”)
// /usr/local/Cellar/go@1.17/1.17.8/libexec/src/strings/reader.go

// NewReader returns a new Reader reading from s.
// It is similar to bytes.NewBufferString but more efficient and read-only.
func NewReader(s string) *Reader { return &Reader{s, 0, -1} }
  • br := bufio.NewReader(s)
// /usr/local/Cellar/go@1.17/1.17.8/libexec/src/bufio/bufio.go

// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) *Reader {
	return NewReaderSize(rd, defaultBufSize)
}

// NewReaderSize returns a new Reader whose buffer has at least the specified
// size. If the argument io.Reader is already a Reader with large enough
// size, it returns the underlying Reader.
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 {
		size = minReadBufferSize
	}
	r := new(Reader)
	r.reset(make([]byte, size), rd)
	return r
}

// 创建  bufio.NewReader 结构,并进行初始化
func (b *Reader) reset(buf []byte, r io.Reader) {
	*b = Reader{
		buf:          buf,
		rd:           r,  // 通过指针形式,引用上面传入的  strings.Reader 结构(即 亲爱的提奥,梵高)
		lastByte:     -1,
		lastRuneSize: -1,
	}
}
2 | n, err := br.ReadString(‘,’) 函数的调用

该函数作用很简单,就是返回被 delim 分割的前半部分数据

本里中【亲爱的提奥,梵高】,delim 为【,】,返回的就是前半部分 【亲爱的提奥】

函数调用逻辑:bufio.ReadString --> bufio.collectFragments --> bufio.ReadSlice --> bufio.fill --> strings.Read

bufio.ReadString:【片段拼接区】对返回的数据片段【二维数组 fullBuffers [][]byte 和 frag】进行拼接,调用 bufio.Write 进行【拼接】返回

bufio.collectFragments :【片段收集区】,由于缓存 buf 固定大小,因此创建二维数组 fullBuffers [][]byte 存储未找到 delim 的 buf,之后刷新 buf 继续寻找,指导找到 delim,此时返回的片段记作【frag】,不记录到二维数组中,之后将 数据片段【二维数组 fullBuffers [][]byte 和 frag】返回上层进行拼接

bufio.ReadSlice:【delim寻找区】

  • 在 buf 中寻找 delim,找到后返回前半部分,记为 frag;
  • 未找到时,
    • 缓存 buf 满的情况,传给 bufio.collectFragments,记录到二维数组 fullBuffers [][]byte
    • 缓存 buf 未满情况,调用 fill 函数,补充缓存 buf,之后继续寻找 delim

**bufio.fill:**就是调用【strings.Read函数】, 补满 buf 缓存区

**strings.Read:**就是我们最开始讲 string 装换为 io.Reader 接口实现的 Read 函数,负责对 string 的读取

因此可以看出,bufio 需要参数 io.Reader 的原因,就是需要【下层函数可以实现基本的数据读取功能】,之后【bufio 函数可以做更多的处理,因为自身携带缓存区 buf,所以读取之后存入缓存 buf,可以进行筛选、截断、甚至写入到其他地方】

所以我们定义一个类型后,【实现其Read 方法func (r *Reader) Read(b []byte) (n int, err error),就是将数据读入到 b []byte中】,之后便可通过bufio.NewReader方法创建bufio.Reader,从而调用 bufio 更多处理功能

2.1 | bufio ReadString
// /usr/local/Cellar/go@1.17/1.17.8/libexec/src/bufio/bufio.go

// ReadString reads until the first occurrence of delim in the input,
// returning a string containing the data up to and including the delimiter.
// If ReadString encounters an error before finding a delimiter,
// it returns the data read before the error and the error itself (often io.EOF).
// ReadString returns err != nil if and only if the returned data does not end in
// delim.
// For simple uses, a Scanner may be more convenient.
func (b *Reader) ReadString(delim byte) (string, error) {
  // collectFragments(delim):以 delim 作为分割符,对传入的数据进行分割
	full, frag, n, err := b.collectFragments(delim)
	// Allocate new buffer to hold the full pieces and the fragment.
	var buf strings.Builder
  // 对 buf 进行扩展,存储被 delim 分割的前部分数据
	buf.Grow(n)   
	// Copy full pieces and fragment in.
  // full 为不包含 delim 的 buf 长度数据
	for _, fb := range full {
		buf.Write(fb)
	}
  // frag 为 buf 中被 delim 截断的前部分数据
	buf.Write(frag)
	return buf.String(), err
}

// Write appends the contents of p to b's buffer.
// Write always returns len(p), nil.
func (b *Builder) Write(p []byte) (int, error) {
	b.copyCheck()
	b.buf = append(b.buf, p...)
	return len(p), nil
}
2.2 | bufio collectFragments 函数
// /usr/local/Cellar/go@1.17/1.17.8/libexec/src/bufio/bufio.go

// collectFragments reads until the first occurrence of delim in the input. It
// returns (slice of full buffers, remaining bytes before delim, total number
// of bytes in the combined first two elements, error).
// The complete result is equal to
// `bytes.Join(append(fullBuffers, finalFragment), nil)`, which has a
// length of `totalLen`. The result is structured in this way to allow callers
// to minimize allocations and copies.
func (b *Reader) collectFragments(delim byte) (fullBuffers [][]byte, finalFragment []byte, totalLen int, err error) {
	var frag []byte
	// Use ReadSlice to look for delim, accumulating full buffers.
	for {
		var e error
    // 按照 delim 分割分隔符 读取一行
		frag, e = b.ReadSlice(delim) 
    
    // 1. 正常情况,直接 break,进行返回
		if e == nil { // got final fragment
			break
		}
    // 2. 其他错误
		if e != ErrBufferFull { // unexpected error
			err = e
			break
		}

		// Make a copy of the buffer.
    // 3. buf 空间不足错误 ErrBufferFull
    // 此种情况就是 buf 是满的,同时没找到 delim 分割符,因此此时要记录此 buf
    // 之后清空 buf,用于读取新数据,继续寻找 delim 分隔符
		buf := make([]byte, len(frag))
		copy(buf, frag)
		fullBuffers = append(fullBuffers, buf)
		totalLen += len(buf)
	}

	totalLen += len(frag)
	return fullBuffers, frag, totalLen, err
}
2.3 | bufio ReadSlice
// ReadSlice reads until the first occurrence of delim in the input,
// returning a slice pointing at the bytes in the buffer.
// The bytes stop being valid at the next read.
// If ReadSlice encounters an error before finding a delimiter,
// it returns all the data in the buffer and the error itself (often io.EOF).
// ReadSlice fails with error ErrBufferFull if the buffer fills without a delim.
// Because the data returned from ReadSlice will be overwritten
// by the next I/O operation, most clients should use
// ReadBytes or ReadString instead.
// ReadSlice returns err != nil if and only if line does not end in delim.
func (b *Reader) ReadSlice(delim byte) (line []byte, err error) {
	s := 0 // search start index
	for {
		// Search buffer.
    // i 为 bytes.IndexByte 返回的最近的 delim 符号位置下标
    // b.r 为缓存 buf 的初始位置,即目前读到的位置
    // b.w 为缓存 buf 的尾部位置,即数据的最大长度
    // s 为上一次,分割掉 字符串的长度,本次不再读取,所以在剩余部分 b.buf[b.r+s:b.w] 中进行分割
    
    // 1. 在 buf 中找到 delim 分割符,此时 i >= 0
		if i := bytes.IndexByte(b.buf[b.r+s : b.w], delim); i >= 0 {
			i += s
      // 返回被 delim 分割的前部分
			line = b.buf[b.r : b.r+i+1]  // 就是目前 被 delim 分割的一行  虽然 return 没有写,但是由于是命名返回值,可以直接返回
			b.r += i + 1
			break
		}
		
    // 2. 在当前 buf 中未找到 delim 分隔符,此时存在两种情况
		// Pending error?
		if b.err != nil {
			line = b.buf[b.r:b.w]
			b.r = b.w
			err = b.readErr()
			break
		}

		// Buffer full?
    // 2.1 在当前 buf 中未找到 delim 分隔符,同时缓存区 buf 还是满的,此时返回错误,交给上层处理
    // 上层会将其存储在 二维数组中  fullBuffers [][]byte
    // 之后上层会继续调用 ReadSlice 函数 寻找 delim
    // 若还没找到 delim,同时缓存区仍满,继续存入 fullBuffers [][]byte
    // 知道最后找 delim ,此时返回的片段叫做 frag []byte
    // 最后 将 fullBuffers [][]byte 和 frag []byte 拼接,就是 delim 分割的前部分
		if b.Buffered() >= len(b.buf) {
			b.r = b.w
			line = b.buf
			err = ErrBufferFull
			break
		}
		
    // 2.2 在当前 buf 中未找到 delim 分隔符,同时缓存区 buf 还是满的,此时清空前面已读的,然后不全buf 进行继续寻找
    
    // s 表示未读部分,但是已扫描过,没有找到 delim 分割符
		s = b.w - b.r // do not rescan area we scanned before

		// 保证缓存空间 buf 一直是满的
    // 前面读走的 b.r 长度数据,在此处进行填充,填充新数据到 buf 的末尾
		b.fill() // buffer is not full
	}

	// Handle last byte, if any.
	if i := len(line) - 1; i >= 0 {
		b.lastByte = int(line[i])
		b.lastRuneSize = -1
	}

	return
}

// Buffered returns the number of bytes that can be read from the current buffer.
func (b *Reader) Buffered() int { return b.w - b.r }
2.4 | bufio fill
// fill reads a new chunk into the buffer.
func (b *Reader) fill() {
	// Slide existing data to beginning.
  // 滑动添加数据,将首位置 b.r 
  // b.r 为缓存 buf 的初始位置,即目前读到的位置
  // b.w 为缓存 buf 的尾部位置,即数据的最大长度
  // b.r 之前为已读过, b.r-b.w 为未读过
  // 此处 copy(b.buf, b.buf[b.r:b.w]) 相当于将未读过的 b.buf[b.r:b.w] 进行前移,这样 buf 后面会空出(已读过的空间 长度为b.r)
  // 前移之后,记得更新下标,目前读到的位置为 0 即 b.r = 0,数据的最大长度也进行更新 b.w -= b.r
	if b.r > 0 {
		copy(b.buf, b.buf[b.r:b.w])
		b.w -= b.r
		b.r = 0
	}

  // 当前数据的最大长度  和 缓存空间 buf 一样大,不需要继续填充
  // panic 表示无法进行填充了
	if b.w >= len(b.buf) {
		panic("bufio: tried to fill full buffer")
	}

	// Read new data: try a limited number of times.
	for i := maxConsecutiveEmptyReads; i > 0; i-- {
    // b.buf[b.w:] 表示 buf 的剩余可填充空间
    // b.rd.Read 调用指针指向的 strings.Read 函数进行填充
		n, err := b.rd.Read(b.buf[b.w:])
		if n < 0 {
			panic(errNegativeRead)
		}
    // 读取数据的时候,会自动给末尾位置  b.w 更新索引下标
		b.w += n
		if err != nil {
			b.err = err
			return
		}
		if n > 0 {
			return
		}
	}
	b.err = io.ErrNoProgress
}
2.5 | strings read
// Read implements the io.Reader interface.
func (r *Reader) Read(b []byte) (n int, err error) {
	if r.i >= int64(len(r.s)) {
		return 0, io.EOF
	}
	r.prevRune = -1
  // r.i 记录了当前数据读到了 哪个位置
  // n 为拷贝数据的长度 
  // copy 第一个参数为 需要填充的, 第二个参数为 被拷贝的数据
  // 若第一个参数空间 < 第二个参数长度,那么只拷贝第一个参数能容纳的长度
  // 如 b 为 []byte 2长度 ,r.s[r.i:]=“adsgsg”
  // 那么执行 copy 后,b 为“ad"
	n = copy(b, r.s[r.i:])
	r.i += int64(n)
	return
}

bufio 包其他功能测试

package main

import (
	"bytes"
	"fmt"
	// "os"
	"strings"
	"bufio"
)

//type Reader struct { ... }

// NewReaderSize 将 rd 封装成一个带缓存的 bufio.Reader 对象,
// 缓存大小由 size 指定(如果小于 16 则会被设置为 16)。
// 如果 rd 的基类型就是有足够缓存的 bufio.Reader 类型,则直接将
// rd 转换为基类型返回。
//func NewReaderSize(rd io.Reader, size int) *Reader

// NewReader 相当于 NewReaderSize(rd, 4096)
//func NewReader(rd io.Reader) *Reader

// bufio.Reader 实现了如下接口:
// io.Reader
// io.WriterTo
// io.ByteScanner
// io.RuneScanner

func testRead(){
	s := strings.NewReader("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890")
	br := bufio.NewReader(s)

	buf:=make([]byte,1024)//sliece
	n, err := br.Read(buf)


	// buf:= [512]byte{}//array
	// n, err := br.Read(buf[:])//将array切片

	fmt.Printf("%s %v %v\n", buf[:n], n, err) // ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 36 <nil>
}

func testReadstring(){
	s := strings.NewReader("亲爱的提奥,梵高")
	br := bufio.NewReader(s)
   // ReadString 在 br 中查找 delim 并返回 delim 及其之前的所有数据。
   // func (b *Reader) ReadString(delim byte) (line []byte, err error)

	n, err := br.ReadString(',')
	fmt.Printf("%s  %v\n", n, err) // 亲爱的提奥,  <nil>

	// 如果未找到 delim 且遇到错误(通常是 io.EOF),则返回缓存中的所
	n, err = br.ReadString(',')
	fmt.Printf("%s  %v\n", n, err) // 梵高  EOF
	fmt.Println(n)
	fmt.Println(string(n))

}

func TestReadBytes() {
	// ReadBytes 功能同 ReadSlice,只不过返回的是缓存的拷贝。
	// func (b *Reader) ReadBytes(delim byte) (line []byte, err error)
	s := strings.NewReader("ABC,你好啊,HIJ")
	br := bufio.NewReader(s)
	line, err := br.ReadBytes(',')
	if err != nil {
	   panic(err)
	} 
	fmt.Printf("%s  %v\n", line, err) //
	line, err = br.ReadBytes(',')
	if err != nil {
	   panic(err)
	} 
	fmt.Printf("%s  %v\n", line, err) //
	fmt.Println(line)
	fmt.Println(string(line))


}
// ReadLine 是一个低水平的行读取原语,大多数情况下,应该使用
// ReadBytes('\n') 或 ReadString('\n'),或者使用一个 Scanner。
//
// ReadLine 通过调用 ReadSlice 方法实现,返回的也是缓存的切片。用于
// 读取一行数据,不包括行尾标记(\n 或 \r\n)。
//
// 只要能读出数据,err 就为 nil。如果没有数据可读,则 isPrefix 返回
// false,err 返回 io.EOF。
//
// 如果找到行尾标记,则返回查找结果,isPrefix 返回 false。
// 如果未找到行尾标记,则:
// 1、缓存不满,则将缓存填满后再次查找。
// 2、缓存是满的,则返回整个缓存,isPrefix 返回 true。
//
// 整个数据尾部“有一个换行标记”和“没有换行标记”的读取结果是一样。
//
// 如果 ReadLine 读取到换行标记,则调用 UnreadByte 撤销的是换行标记,
// 而不是返回的数据。
func testReadline(){
	s := strings.NewReader("ABC,你好啊,HIJ,李银河")
	br := bufio.NewReader(s)
	var finish bool	
	line,finish ,err := br.ReadLine()
	if err != nil {
	   panic(err)
	} 
	fmt.Println(line,finish)
	fmt.Println(string(line))
}



//type Writer struct { ... }

// NewWriterSize 将 wr 封装成一个带缓存的 bufio.Writer 对象,
// 缓存大小由 size 指定(如果小于 4096 则会被设置为 4096)。
// 如果 wr 的基类型就是有足够缓存的 bufio.Writer 类型,则直接将
// wr 转换为基类型返回。
//func NewWriterSize(wr io.Writer, size int) *Writer

// NewWriter 相当于 NewWriterSize(wr, 4096)
//func NewWriter(wr io.Writer) *Writer

//func NewWriter(wr io.Writer) *Writer

// bufio.Writer 实现了如下接口:
// io.Writer
// io.ReaderFrom
// io.ByteWriter

// WriteString 功能同 Write,只不过写入的是字符串
// func (b *Writer) WriteString(s string) (int, error)

// WriteRune 向 b 写入 r 的 UTF-8 编码,返回 r 的编码长度。
// func (b *Writer) WriteRune(r rune) (size int, err error)

// Flush 将缓存中的数据提交到底层的 io.Writer 中
// func (b *Writer) Flush() error

// Available 返回缓存中未使用的空间的长度
// func (b *Writer) Available() int

// Buffered 返回缓存中未提交的数据的长度
// func (b *Writer) Buffered() int

// Reset 将 b 的底层 Writer 重新指定为 w,同时丢弃缓存中的所有数据,复位
// 所有标记和错误信息。相当于创建了一个新的 bufio.Writer。
// func (b *Writer) Reset(w io.Writer)

func TestWriteTo() {
	// WriteTo方法实现了io.WriterTo接口。
	// func (b *Reader) WriteTo(w io.Writer) (n int64, err error)
	s := strings.NewReader("李银河")
	br := bufio.NewReader(s)
	b := bytes.NewBuffer(make([]byte, 0))
	br.WriteTo(b)
	fmt.Println(b)
	fmt.Printf("%T\n",b)
	fmt.Println(b.String())
} 




func TestNewWriter() {
 
	// NewWriter创建一个具有默认大小缓冲、写入w的*Writer。
	// 相当于 NewWriterSize(wr, 4096)
	// func NewWriter(w io.Writer) *Writer
  
	// Buffered()返回缓冲中已使用的字节数。
	// func (b *Writer) Buffered() int
  
	// Available()返回缓冲中还有多少字节未使用。
	// func (b *Writer) Available() int
  
	// Reset丢弃缓冲中的数据,清除任何错误,将b重设为将其输出写入w。
	// func (b *Writer) Reset(w io.Writer)
  
	b := bytes.NewBuffer(make([]byte, 0))
	bw := bufio.NewWriter(b)
  
	fmt.Println(bw.Available(), bw.Buffered()) // 4096  0
	bw.WriteString("card")
	fmt.Println(bw.Available(), bw.Buffered()) // 4092  4
  
	bw.Reset(b)
	fmt.Println(bw.Available(), bw.Buffered()) // 4096  0
	fmt.Println(bw)

}


func TestWriteString() {
	// WriteString 同 Write,只不过写入的是字符串
	// func (b *Writer) WriteString(s string) (int, error)
	b := bytes.NewBuffer(make([]byte, 0))
	bw := bufio.NewWriter(b)
	bw.WriteString("hello world")
	bw.Flush()//y一定要flush到底层//y一定要flush到底层//y一定要flush到底层
	fmt.Printf("%s\n", b)
} 

func TestWritebytes() {
	// WriteString 同 Write,只不过写入的是字符串
	// func (b *Writer) WriteString(s string) (int, error)
	b := bytes.NewBuffer(make([]byte, 0))
	bw := bufio.NewWriter(b)
	bw.WriteByte('h')
	bw.Flush()//y一定要flush到底层//y一定要flush到底层//y一定要flush到底层
	fmt.Printf("%s\n", b)
} 
func TestWriterune() {
	// WriteString 同 Write,只不过写入的是字符串
	// func (b *Writer) WriteString(s string) (int, error)
	b := bytes.NewBuffer(make([]byte, 0))
	bw := bufio.NewWriter(b)
	bw.WriteRune('h')
	bw.Flush()//y一定要flush到底层//y一定要flush到底层//y一定要flush到底层
	fmt.Printf("%s\n", b)
} 



func TestReadFrom() {
	// ReadFrom实现了io.ReaderFrom接口。
	//将r读到b
	// func (b *Writer) ReadFrom(r io.Reader) (n int64, err error)
	// ReadFrom无需使用Flush
	// b := bytes.NewBuffer(make([]byte, 0))
	b :=bytes.NewBufferString("你好啊")
	s := strings.NewReader(",李银河")
	bw := bufio.NewWriter(b)
	bw.ReadFrom(s)
	fmt.Println(b)
} 


 
func TestReadWriter() {
	// ReadWriter类型保管了指向Reader和Writer类型的指针
	// 实现了io.ReadWriter接口。
  
	// NewReadWriter 生成bufio.ReadWriter对象
	// func NewReadWriter(r *Reader, w *Writer) *ReadWriter
	b := bytes.NewBuffer(make([]byte, 0))
	bw := bufio.NewWriter(b)
	s := strings.NewReader("hello world")
	br := bufio.NewReader(s)
	rw := bufio.NewReadWriter(br, bw)
  
	word, err := rw.ReadString(' ')
	if err != nil {
	   panic(err)
	}
	fmt.Printf("%s\n", word) // hello
  
	_, err = rw.WriteString(",I'm coming")
	if err != nil {
	   panic(err)
	}
	rw.Flush()
	fmt.Println(b)
}



func TestNewScanner()  {
	// Scanner 提供了一个方便的接口来读取数据,例如遍历多行文本中的行。Scan 方法会通过
	// 一个“匹配函数”读取数据中符合要求的部分,跳过不符合要求的部分。“匹配函数”由调
	// 用者指定。本包中提供的匹配函数有“行匹配函数”、“字节匹配函数”、“字符匹配函数”
	// 和“单词匹配函数”,用户也可以自定义“匹配函数”。默认的“匹配函数”为“行匹配函
	// 数”,用于获取数据中的一行内容(不包括行尾标记)
	//
	// Scanner 使用了缓存,所以匹配部分的长度不能超出缓存的容量。默认缓存容量为 4096 -
	// bufio.MaxScanTokenSize,用户可以通过 Buffer 方法指定自定义缓存及其最大容量。
	//
	// Scan 在遇到下面的情况时会终止扫描并返回 false(扫描一旦终止,将无法再继续):
	// 1、遇到 io.EOF
	// 2、遇到读写错误
	// 3、“匹配部分”的长度超过了缓存的长度
	//
	// 如果需要对错误进行更多的控制,
	// 或“匹配部分”超出缓存容量,或需要连续扫描,
	// 则应该使用 bufio.Reader
	// func NewScanner(r io.Reader) *Scanner
  
	// Bytes方法返回最近一次Scan调用生成的token。
	// 底层数组指向的数据可能会被下一次Scan的调用重写。
	// func (s *Scanner) Bytes() []byte
  
  
	// Buffer()方法设置扫描时使用的初始缓冲区和最大值
	// 默认情况下,Scan使用内部缓冲区并设置MaxScanTokenSize的最大令牌大小
	s := strings.NewReader("周起\n卡牌\n程序员\n")
	bs := bufio.NewScanner(s)
	bs.Buffer(make([]byte,0),bufio.MaxScanTokenSize)
	for bs.Scan() {
	   // 周起
	   // 卡牌
	   // 程序员
	   fmt.Printf("%s\n", bs.Bytes())
	}
 
}

func TestScan()  {
	// Scan方法获取当前位置的token(该token可以通过Bytes或Text方法获得),
	// 并让Scanner的扫描位置移动到下一个token。
	// 当扫描因为抵达输入流结尾或者遇到错误而停止时,
	// 本方法会返回false。在Scan方法返回false后,
	// Err方法将返回扫描时遇到的任何错误;
	// 除非是io.EOF,此时Err会返回nil。
	// func (s *Scanner) Scan() bool
	s := strings.NewReader("周起 卡牌 程序员")
	bs := bufio.NewScanner(s)
	bs.Split(bufio.ScanWords)
	for bs.Scan()  {
	   fmt.Printf("%s %s\n", bs.Text(), bs.Bytes())
	}
} 

  
 func TestScanBytes()  {
	// Bytes方法返回最近一次Scan调用生成的token。
	// 底层数组指向的数据可能会被下一次Scan的调用重写。
	s := strings.NewReader("abcd")
	bs := bufio.NewScanner(s)
	bs.Split(bufio.ScanBytes)
	for bs.Scan(){
	   // a
	   // b
	   // c
	   // d
	   fmt.Printf("%s\n", bs.Bytes())
	}
 }
  
 func TestScanRunes()  {
	// ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),
	// 本函数会将每个utf-8编码的unicode码值作为一个token返回。
	s := strings.NewReader("周起卡牌程序员")
	bs := bufio.NewScanner(s)
	bs.Split(bufio.ScanRunes)
	for bs.Scan()  {
	   // 周
	   // 起
	   // 卡
	   // 牌
	   // 程
	   // 序
	   // 员
	   fmt.Printf("%s\n", bs.Text())
	}
 }
  
 func TestScanWords()  {
	// ScanRunes是用于Scanner类型的分割函数(符合SplitFunc),
	// 本函数会将空白(参见unicode.IsSpace)
	// 分隔的片段(去掉前后空白后)作为一个token返回。
	// 本函数永远不会返回空字符串。
	s := strings.NewReader("我 是 卡 牌")
	bs := bufio.NewScanner(s)
	bs.Split(bufio.ScanWords)
	for bs.Scan(){
	   // 我
	   // 是
	   // 卡
	   // 牌
	   fmt.Printf("%s\n", bs.Text())
	}
 }
  
 func TestScanLines()  {
	// 将每一行文本去掉末尾的换行标记作为一个token返回
	// 此函数的bs.Scan()的默认值
	s := strings.NewReader("卡牌\n周起\n程序员\n")
	bs := bufio.NewScanner(s)
	bs.Split(bufio.ScanLines)
	for bs.Scan(){
	   // 卡牌
	   // 周起
	   // 程序员
	   fmt.Printf("%s\n", bs.Text())
	}
} 

func main(){
	testRead()
	testReadstring()
	TestReadBytes()
	testReadline()
	TestWriteTo() 
	TestWriteString()
	TestWritebytes()
	TestWriterune()
	TestReadFrom()
	TestScan()
	TestNewScanner() 
	TestScanBytes()
	TestScanWords()
	TestScanRunes()
	TestScanLines()
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值