从存储区提供程序的数据读取器中进行读取时出错_[译]Go 语言中的流式 IO

原文链接

  • Streaming IO in Go – Learning the Go Programming Language – Medium

以下为译文

在 Go 中,输入输出操作是通过能读能写的字节流数据模型来实现的。为此,io 包提供了 io.Reader 和 io.Writer 接口来进行输入输出操作,如下所示:

7bcdc56c107bf7f1d499a74337002df7.png

Go 附带了许多 API,这些 API 支持来自内存结构,文件,网络连接等资源的流式 IO。本文重点介绍如何自定义实现以及使用标准库中的 io.Reader 和 io.Writer接口创建能够传输流式数据的 Go 程序

io.Reader

由 io.Reader 接口表示的读取器将数据从某些源读取到缓冲区,可以像用水管输送水流一样来传送它,如下所示

2d5bf978072938e11180ecce908dbf82.png

对于要用作读取器的类型,它必须从接口 io.Reader 实现 Read(p [] byte)方法,如下所示:

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

Read() 方法的实现应返回读取的字节数或发生的错误。如果数据源已输出全部内容,则 Read 应返回 io.EOF

读取规则(补充)

在 Reddit 反馈之后,我决定添加有关读取规则的这一部分。读取器的行为取决于它的实现,但是你应该知道从读取器读取数据时, io.Reader 中的一些规则:

译者注:p 为缓冲区,n 为字节数
  1. 如果可能,Read() 将读取 len(p) 到 p
  2. 调用 Read() 后,返回的字节数 n 可能小于 len(p)
  3. 出错时,Read() 仍可在缓冲区 p 中返回 n 个字节。例如,从突然关闭的 TCP 套接字读取。取决于您的程序设计,您可以选择将字节保存在 p 中或重新尝试从 TCP 套接字中读取
  4. 当 Read() 读完所有可用数据时,读取器可能返回非零 n 和 err = io.EOF。尽管如此,您可以自己实现返回规则,如可以选择在流的末尾返回非零 n 和 err = nil。在这种情况下,任何后续读取必须返回 n = 0,err = io.EOF
  5. 最后,调用 Read() 返回 n = 0 和 err = nil 并不意味着 EOF,因为下一次调用 Read() 可能会返回更多数据

如您所见,直接从读取器读取流数据可能会非常棘手。幸运的是,标准库中的读取器使用的一些方法使其易于流式传输。不过,在使用读取器之前,请查阅其文档

从读取器中流式传输数据

直接从读取器流式传输数据很容易。Read 方法被设计为在循环内调用,每次迭代时,它从源读取一大块数据并将其放入缓冲区 p 中。直到 Read 方法返回io.EOF 错误

以下是一个简单的示例,它使用 string.NewReader(string) 创建的字符串读取器来从字符串源中流式传输字节值:

func main() {
    
	reader := strings.NewReader("Clear is better than clever")
	p := make([]byte, 4)
	for {
    
		n, err := reader.Read(p)
		if err == io.EOF {
    
			break
		}
		fmt.Println(string(p[:n]))
	}
}

上面的源代码用 make([] byte,4) 创建一个 4 字节长的传输缓冲区 p。缓冲区故意保持小于字符串源的长度, 这是为了演示如何从大于缓冲区的源正确传输数据块

更新: Reddit 上有人指出上面的代码中有 bug, 它永远不会捕获非零错误 err != io.EOF . 以下修复了代码:

func main() {
    
	reader := strings.NewReader("Clear is better than clever")
	p := make([]byte, 4)
	
	for {
    
		n, err := reader.Read(p)
		if err != nil{
    
		    if err == io.EOF {
    
			fmt.Println(string(p[:n])) //should handle any remainding bytes.
			break
		    }
		    fmt.Println(err)
		    os.Exit(1)
		}
		fmt.Println(string(p[:n]))
	}
}

自定义一个 io.Reader

上一节使用标准库中的现有 IO 读取器实现。现在,让我们看看如何编写自己的读取器。以下是 io.Reader 的简单实现,它从流中过滤掉非字母字符。

package main

import (
    "fmt"
    "io"
)

// alphaReader is a simple implementation of an io.Reader
// that streams only alpha chars from its string source.
type alphaReader struct {
    
    src string
    cur int
}

func newAlphaReader(src string) *alphaReader {
    
    return &
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值