一些我不怎么熟悉的接口和方法的总结
接口 io.Seeker 1 2 3 type Seeker interface { Seek(offset int64 , whence int ) (ret int64 , err error) }
第一个参数是偏移量,第二个参数有几个选项,如下
1 2 3 4 5 6 const ( SeekStart = 0 SeekCurrent = 1 SeekEnd = 2 )
strings.NewReader
实现了该接口,所以使用它来写 demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 func main () { r := strings.NewReader("abcedfg" ) readBuf := make ([]byte , 2 ) _, _ = r.Seek(2 , io.SeekStart) _, _ = r.Read(readBuf) fmt.Println(string (readBuf)) _, _ = r.Seek(1 , io.SeekCurrent) _, _ = r.Read(readBuf) fmt.Println(string (readBuf)) _, _ = r.Seek(1 , io.SeekStart) _, _ = r.Read(readBuf) fmt.Println(string (readBuf)) _, _ = r.Seek(-2 , io.SeekEnd) _, _ = r.Read(readBuf) fmt.Println(string (readBuf)) }
工具方法 io.LimitedReader 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type LimitedReader struct { R Reader N int64 } func (l *LimitedReader) Read (p []byte ) (n int , err error) { if l.N <= 0 { return 0 , EOF } if int64 (len (p)) > l.N { p = p[0 :l.N] } n, err = l.R.Read(p) l.N -= int64 (n) return }
用法:
当 N 比数据总量大时是全部读取的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 func main () { str := "abcdefghijklmnop" r := io.LimitReader(bytes.NewReader([]byte (str)), int64 (len (str)+5 )) for { buf := make ([]byte , 5 ) n, err := r.Read(buf) if err == io.EOF { break } if err != nil { log.Fatal(err) } fmt.Println(string (buf[:n])) } }
当把 io.LimitReader
的第二个参数改成小于字符串长度时,将会仅读取指定长度的字符
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func main () { str := "abcdefghijklmnop" r := io.LimitReader(bytes.NewReader([]byte (str)), 3 ) for { buf := make ([]byte , 10 ) n, err := r.Read(buf) if err == io.EOF { break } if err != nil { log.Fatal(err) } fmt.Println(string (buf[:n])) } }
io.SectionReader 实现了如下接口
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 type Reader interface { Read(p []byte ) (n int , err error) } type Seeker interface { Seek(offset int64 , whence int ) (int64 , error) } type ReadSeeker interface { Reader Seeker } type ReaderAt interface { ReadAt(p []byte , off int64 ) (n int , err error) }
传入的输入数据需要实现 io.ReaderAt
接口
1 2 3 4 5 func NewSectionReader (r ReaderAt, off int64 , n int64 ) *SectionReader { return &SectionReader{r, off, off, off + n} }
使用样例
1 2 3 4 5 6 str := "abcdefg" r := io.NewSectionReader(strings.NewReader(str), 2 , 3 ) _, _ = io.Copy(os.Stdout, r) fmt.Println() _, _ = r.Seek(0 , io.SeekStart) _, _ = io.Copy(os.Stdout, r)
io.teeReader 将一个 Reader 读到的数据输出到一个 Writer 中,边读边写,实现比较简单,源码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 func TeeReader (r Reader, w Writer) Reader { return &teeReader{r, w} } type teeReader struct { r Reader w Writer } func (t *teeReader) Read (p []byte ) (n int , err error) { n, err = t.r.Read(p) if n > 0 { if n, err := t.w.Write(p[:n]); err != nil { return n, err } } return }
有一个应用场景是,读取文件上传到服务器,同时计算 md5 ,当上传完成时服务器会返回一个 md5 ,然后对这两个值进行比较,验证传输的数据无误。此时就可以边读取上传数据边计算文件的 md5 值,省去再读取一遍文件的操作
1 2 3 4 5 6 7 8 func main () { f, _ := os.Open("teereader.go" ) defer f.Close() m := md5.New() chekcsum, _ := PostToServer(io.TeeReader(f, m)) md5Hash := hex.EncodeToString(m.Sum(nil )) Compare(md5Hash, chekcsum) }
io.multiReader & io.multiWriter 同时从多个 reader 读数据以及同时向多个 writer 写数据(这个类似于 tee)
io.PipeReader & io.PipeWriter 使用 io.Pipe
创建
Ref Go语言标准库
Go语言中的io.Reader和io.Writer以及它们的实现 | 鸟窝