Passing a buffer of length 1 to bufio.Read
, when the reader is backed with an underlying os.File, will indeed return n==0, io.EOF
if the file is at EOF.
The documentation is being a bit imprecise because some of the behavior depends on the underlying reader you pass to the bufio
reader. The code for bufio.Read()
draws a more accurate picture. I'll outline the logic.
bufio.Read
: Only issues a Read
to the underlying reader if all bytes in the internal buffer have been exhausted. So, presumably, if you've already read as many bytes from the buffered reader as the number of bytes in the underlying file, that internal buffer should be exhausted when you make the last call bufio.Read(buf[0:1])
to check for EOF.
When the internal buffer is exhausted, and you ask the bufio
reader for more, bufio.Read
will do at most one call to the underlying reader. The type of error you get then will depend on your underlying reader.
Asking to read for n > 0
bytes from an os.File
when the read pointer is already at EOF should return 0, io.EOF
(according to the doc on os.File File.Read
). But if your underlying reader was something else, perhaps a custom type specific to your application designed to return 0, nil
at EOF, then bufio.Read
would echo that back instead.
bufio.ReadByte
: The logic behind bufio.ReadByte
is slightly different but the outcome should be the same as bufio.Read
in cases where the underlying reader is an os.File
. The main difference with bufio.Read
is that bufio.ReadByte
can make several attempts to refill the internal buffer. If an error is encountered during refilling (which will be the case for a os.File
reader at EOF), it is returned after the first erroneous read attempt. So, if your underlying Reader is an os.File
reader, then you'll get 0, io.EOF
if and only if your underlying file is at EOF. If your underlying reader was a custom reader type that only returned 0, nil
at EOF, then bufio.ReadByte
would eventually emit a "NoProgress" error. I'm not sure why the retry logic is only in bufio.ReadByte
, but the good news is that either option can be used if your underlying file behaves like an os.File
.
Other info:
This is not directly applicable to golang, but you may find the following thread interesting: Can read(2) return zero bytes when not at EOF. Its topic is the read() system call's semantics (POSIX). Reads on non-blocking sockets/files, even when no data is ready, should return -1, not 0, and set errno EAGAIN (or EINTR when interrupted). Non-blocking sockets/files are not really a concept native to go (as far as i know), and the bufio
module in particular will panic()
whenever/if the underlying reader returns negative numbers, so you don't have to worry about it.