不知道为什么,但我的io.Reader实现似乎存在某种缺陷?

io.Reader的文档说明返回非零字节计数和非零错误应该是正常的:

它可以从同一调用返回(非零)错误,或者从后续调用返回错误(且n==0).这种一般情况的一个例子是,在输入流的末尾返回非零字节数的读取器可能返回ERR==EOF或ERR==NIL.下一次读取应返回0,EOF.

在考虑错误之前,调用方应始终处理返回的n>0字节.这样做可以正确地处理在读取某些字节之后发生的I/O错误以及允许的两种EOF行为.

但这对fmt.Fscan不起作用,而是挂起了程序:

package main

import (
    "fmt"
    "io"
)

type Byte byte

func (b Byte) Read(p []byte) (n int, err error) {
    if len(p) == 0 {
        return 0, io.ErrShortBuffer
    }
    p[0] = byte(b)
    return 1, io.EOF
}

func main() {
    var n int
    b := Byte('9')
    z, err := fmt.Fscan(b, &n)
    fmt.Println(n, z, err)
}

当然,如果我单独返回一个包含io.EOF的零字节数,那么它是有效的:

type Byte struct {
    v   byte
    eof bool
}

func (b *Byte) Read(p []byte) (n int, err error) {
    if len(p) == 0 {
        return 0, io.ErrShortBuffer
    }
    if b.eof {
        return 0, io.EOF
    }
    p[0] = b.v
    b.eof = true
    return 1, nil
}

func main() {
    var n int
    b := Byte{v: '9'}
    z, err := fmt.Fscan(&b, &n)
    fmt.Println(n, z, err)
}

我的原始实现中是否存在缺陷,或者我是否应该简单地不依赖于这位记录在案的io.Reader行为,并在没有更多数据可读时始终单独返回0, io.EOF

推荐答案

fmt.Scanf可以正确地处理返回COUNT和io.EOF,但您的读取器即使在返回io.EOF之后也会继续返回值.

因为扫描器实现依赖于使用io.ReadFull,而io.ReadFull使用io.ReadAtLeast,所以您将需要一个更完整的实现来处理重复读取.您可以通过使用跟踪EOF的扩展版本来测试这一点,并在第一个Read上返回io.EOF,它仍然可以像预期的那样与fmt.Fscan一起工作.

这些文档的主要摘录如下:

io.ReadFull:

...它不会将读取的EOF视为要报告的错误

io.ReadAtLeast:

只有在没有读取字节的情况下,错误才是EOF.

因为这io个帮助器需要自己解释io.EOF,所以它们的调用者只能查找返回的实际数据,并且由于您的读取器继续返回数据,它们将无限期地重复调用.通过在读取器上重复调用io.ReadAll可以很容易地演示这一点,该读取器每次返回另一个值.

b := Byte('9')
fmt.Println(io.ReadAll(b))
fmt.Println(io.ReadAll(b))
fmt.Println(io.ReadAll(b))

// [57] <nil>
// [57] <nil>
// [57] <nil>

https://go.dev/play/p/It_4BWLsSP8

Go相关问答推荐

Go程序在并发Forking 循环中停留在syscall.Wait4

如何使用Gorilla WebSockets实现Http.Hijacker&;alexedwards/scs/v2

在Golang中Mergesort的递归/并行实现中出现死锁

golang有int32溢出吗?

转到http服务器头内容-类型设置为多部分/表单-数据,但在客户端获取内容-类型:文本/纯文本

Golang:访问any类型泛型上的字段

我们如何保证取消的上下文会导致 goroutine 终止?

如何为ANTLR4目标Go调试监听器

使用 httptest 对 http 请求进行单元测试重试

Go 切片容量增长率

在 .go 文件中运行一个函数和在 Go 模板中调用它有什么区别?

Golang 中具体类型的错误片段

具有嵌套重复的正则表达式

GOLANG:为什么 SetDeadline/SetReadDeadline/SetWriteDeadline 在使用 os.File.Fd() 时对文件不起作用?

如何在没有内存分配的情况下压缩和发布文件

在 go 中将运行命令的标准输出发送到其标准输入

如何优雅地映射到 Go 中返回可变长度数组的方法?

GoLang 遍历 yaml 文件

退格字符在围棋操场中不起作用

如何使用 httputil.ReverseProxy 设置 X-Forwarded-For