英文:
io.Reader endless loop with fmt.Fscan
问题
不确定为什么,但我的io.Reader
实现似乎有些问题?
io.Reader
的文档说明返回非零字节数和非nil错误应该是可以的:
它可以在同一次调用中返回(非nil)错误,也可以在后续调用中返回错误(且n == 0)。这种一般情况的一个实例是,返回输入流末尾的非零字节数的Reader可以返回err == EOF或err == nil。下一次读取应该返回0,EOF。
调用者在考虑错误err之前应始终处理返回的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
?
英文:
Not sure why, but my io.Reader
implementation appears to be flawed somehow?
The docs for io.Reader
state that returning a non-zero byte count and a non-nil error should be ok:
> It may return the (non-nil) error from the same call or return the error (and n == 0) from a subsequent call. An instance of this general case is that a Reader returning a non-zero number of bytes at the end of the input stream may return either err == EOF or err == nil. The next Read should return 0, EOF.
>
> Callers should always process the n > 0 bytes returned before considering the error err. Doing so correctly handles I/O errors that happen after reading some bytes and also both of the allowed EOF behaviors.
But this doesn't work for fmt.Fscan
, instead hanging the program:
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)
}
Of course, it works if i instead return a zero byte count with io.EOF
separately:
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)
}
Is there a flaw in my original implementation, or should i simply not depend on this particular bit of documented behavior of io.Reader
and always separately return 0, io.EOF
when there is no more data to read?
答案1
得分: 3
fmt.Scanf
确实能正确处理返回计数和io.EOF
,但是你的读取器在io.EOF
之后仍然返回值。
因为扫描器的实现依赖于使用io.ReadFull
,而io.ReadFull
又使用了io.ReadAtLeast
,所以你需要一个更完整的实现来处理重复读取。你可以通过使用跟踪EOF的扩展版本,并在第一次Read
时返回io.EOF
来进行测试,它仍然可以按预期与fmt.Fscan
一起工作。
以下是文档中的关键摘录:
io.ReadFull
:
> ...它不会将来自Read的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
英文:
fmt.Scanf
does handle returning the count and io.EOF
correctly, but your reader continues to return values even after io.EOF
.
Because the scanner implementation relies on using io.ReadFull
, which uses io.ReadAtLeast
, you are going need a more complete implementation to work with repeated reads. You can test this by taking your extended version which tracks EOF, and return io.EOF
on the first Read
and it will still work with fmt.Fscan
as expected.
Key excerpts from the docs:
io.ReadFull
:
> ... it does not treat an EOF from Read as an error to be reported
io.ReadAtLeast
:
> The error is EOF only if no bytes were read.
Because these io
helpers need to interpret io.EOF
themselves, their callers can only look for the actual data returned, and since your reader continues to return data, they will repeat the call indefinitely. This is easily demonstrated by calling io.ReadAll
repeatedly on your reader, which returns another value every time.
b := Byte('9')
fmt.Println(io.ReadAll(b))
fmt.Println(io.ReadAll(b))
fmt.Println(io.ReadAll(b))
// [57] <nil>
// [57] <nil>
// [57] <nil>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论