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.
I don’t know why, but my io.reader
implementation seems to have some kind of flaw?
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
?
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!