参考
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
- 首先
s := strings.NewReader("亲爱的提奥,梵高")
创建了*strings.Reader 结构
,并进行了初始化,记录了原始文本亲爱的提奥,梵高
- 之后
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()
}