Home >Backend Development >Golang >io.Reader and fmt.Fscan infinite loop

io.Reader and fmt.Fscan infinite loop

WBOY
WBOYforward
2024-02-09 17:45:191200browse

io.Reader 与 fmt.Fscan 无限循环

php editor Strawberry will introduce to you the infinite loop problem of io.Reader and fmt.Fscan in this article. When using the fmt.Fscan function to read input, if the read content does not match the input format, an infinite loop will occur. This problem may cause us a lot of trouble, but with some tips and precautions, we can easily solve this problem. Next, we will explain in detail how to avoid infinite loops in io.Reader and fmt.Fscan to help you better use these two functions.

Question content

I don’t know why, but my io.reader implementation seems to have some kind of flaw?

The documentation for

io.reader states that it should be fine to return a non-zero byte count and a non-zero error:

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 has no effect on fmt.fscan, instead it hangs 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, if I use io.eof by itself it works if it returns a zero byte count:

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 not rely on this specific logging behavior of io.reader and always return alone when there is no more data to read 0, io.eof?

Workaround

fmt.scanf does handle return count and io.eof correctly, but even in io.eof Afterwards, your reader continues to return values.

Since the scanner implementation relies on using io.readfull, which uses io.readatleast, you will need a more complete implementation to handle duplicate reads. You can test this by using an extended version that traces eof and on the first read returns io.eof it will still work as expected with fmt.fscan use together.

Main excerpts from the documentation:

io.readfull

...it does not treat eof in read as an error to report

io.readatleast

The error will be eof only if no bytes were read.

Because these io helpers need to interpret io.eof themselves, their callers can only look up the actual data returned, and since your reader continues to return data, They will be called repeatedly indefinitely. This can be easily demonstrated by calling io.readall repeatedly on the reader, returning another value each 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>

https://www.php.cn/link/ad6fff7b7be06acff1c63ced9f0da4ea

The above is the detailed content of io.Reader and fmt.Fscan infinite loop. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete