[关闭]
@zwh8800 2016-08-10T14:24:10.000000Z 字数 2583 阅读 295541

总结 golang 对于 stream 的抽象

blog golang stream io 标准库


本文对 golang 标准库中的 stream 进行了一些总结。


Interfaces

在 golang 中,通过几个基本的 interface 对流操作进行了抽象。

读写

首先是最基本的Reader、Writer,定义了对于一个流来说最基本的操作:读、写。这两个 interface 定义在 io 包里。

  1. type Reader interface {
  2. Read(p []byte) (n int, err error)
  3. }
  4. type Writer interface {
  5. Write(p []byte) (n int, err error)
  6. }

Seeker、ReaderAt、WriterAt、Closer

更进一步的,最常见的流就是文件了。对于文件来说,除了简单的读写操作之外,还有 Seek、ReadAt、WriteAt、Close 操作。标准库对这些操作也进行了抽象。

  1. type Seeker interface {
  2. Seek(offset int64, whence int) (int64, error)
  3. }
  4. type ReaderAt interface {
  5. ReadAt(p []byte, off int64) (n int, err error)
  6. }
  7. type WriterAt interface {
  8. WriteAt(p []byte, off int64) (n int, err error)
  9. }
  10. type Closer interface {
  11. Close() error
  12. }

组合

有了这些基础设施之后,就可以使用 golang 的组合大法了:

  1. type ReadCloser interface {
  2. Reader
  3. Closer
  4. }
  5. type ReadSeeker interface {
  6. Reader
  7. Seeker
  8. }
  9. type WriteCloser interface {
  10. Writer
  11. Closer
  12. }
  13. type WriteSeeker interface {
  14. Writer
  15. Seeker
  16. }
  17. type ReadWriter interface {
  18. Reader
  19. Writer
  20. }
  21. type ReadWriteCloser interface {
  22. Reader
  23. Writer
  24. Closer
  25. }
  26. type ReadWriteSeeker interface {
  27. Reader
  28. Writer
  29. Seeker
  30. }

杂项

其他还有一些不很常用的操作。

写到一个Writer中从一个Reader中读取。这两个操作会自动判断EOF,如果没有把所有数据写完/读完,就会继续写/读。

  1. type WriterTo interface {
  2. WriteTo(w Writer) (n int64, err error)
  3. }
  4. type ReaderFrom interface {
  5. ReadFrom(r Reader) (n int64, err error)
  6. }

还有一些面向 byterune 的读写操作:

  1. type ByteReader interface {
  2. ReadByte() (c byte, err error)
  3. }
  4. type ByteScanner interface {
  5. ByteReader
  6. UnreadByte() error
  7. }
  8. type ByteWriter interface {
  9. WriteByte(c byte) error
  10. }
  11. type RuneReader interface {
  12. ReadRune() (r rune, size int, err error)
  13. }
  14. type RuneScanner interface {
  15. RuneReader
  16. UnreadRune() error
  17. }

Scanner 允许把一个读出的字节重新放回流中。这个操作有点类似 Peek 但是比 Peek 别扭一些。这种操作在做词法分析器的时候很有用。

下面是一些这些 interface 的实现。


文件

使用 os.Openos.OpenFile 可以打开一个文件进行读写。它返回一个 *os.File 结构体,这个结构体实现了上面除了杂项外的接口。

管道

使用 os.Pipe 可以创建一个操作系统提供的管道(参见 unix 管道)。这个函数也是返回一个 *os.File 结构体。

网络

net.Conn 是个 interface,他也实现了 io.Readerio.Writerio.Closer 这三个接口。

内存流

有时候我们需要把一段内存当作流来处理,我们把这种设施叫做内存流。内存流在某些情况下非常有用。

不阻塞的内存流

strings 包中,strings.Reader 实现了 io.Readerio.Seekerio.ReaderAtio.WriterToio.ByteScannerio.RuneScanner 这些接口。

可以将一个字符串当作一个只读流来使用。

bytes 包中提供了一个比 strings.Reader 更高级的内存流-- bytes.Buffer。它支持读写操作,同时还可以讲写入的数据转换成字符串来使用。这个结构体一般会被当做 golang 中的 StringBuilder 使用。

另外,如果需要将 []byte 转换为只读流,可以使用 bytes.Reader 它和 strings.Reader 类似。当数据只需要进行读操作时,使用这两个 Reader 会比 Buffer 要高效一些。

这些内存流都是非阻塞的,如果内存中没有数据了,会立即返回一个 EOF 错误。

阻塞的内存流

有时我们需要一个可以阻塞的内存流。当 buffer 中无数据的时候,Read 操作会被阻塞住;当 buffer 满时,Write 操作也会阻塞。

io.Pipe 提供了这个功能。

  1. func Pipe() (*PipeReader, *PipeWriter)

使用 io.Pipe 函数创建一对 pipe,对 PipeReader 进行读操作,对
PipeWriter 进行写操作。


杂七杂八的功能

io.LimitReader 函数可以限制一个 Reader 的读取字节数

io.TeeReader 可以在你读一个 Reader 的同时,将数据写入到一个 Writer 中:

  1. func teeExample(input io.Reader) {
  2. backup := os.Create("xxx.log")
  3. r := io.TeeReader(input, backup)
  4. fmt.Println(io.ReadAll(r))
  5. }

这个例子可以将 input 的内容同时写到 console 和 xxx.log 文件中。

io.MultiReaderio.MultiWriter 函数可以将多个 Reader 或 Writer 合并成一个 Reader 或 Writer。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注