in, out := bufio.NewReader(os.Stdin), bufio.NewWriter(os.Stdout)
for {
c, err := in.ReadByte()
if err == io.EOF {
break
}
out.WriteByte(c)
}
I want to read bytewise from the stdin stream. Unlike the Read
methodReadByte
doesn't seem to return io.EOF
. How can I break if all bytes have been read?
This is not an issue of the Reader.ReadByte()
implementation, nor that of bufio.NewReader()
.
See this example to prove it:
buf := bytes.NewBufferString("Hello World!\n")
in := bufio.NewReader(buf)
for {
c, err := in.ReadByte()
if err == io.EOF {
break
}
fmt.Print(string(c))
}
When running, the above prints
Hello World!
And terminates properly.
Your issue is with os.Stdin
. Reading from it is specific to its source. If it is your terminal, reading from it simply blocks and does not report io.EOF
. See this example to prove it:
in := bufio.NewReader(os.Stdin)
for {
fmt.Println("Reading.")
c, err := in.ReadByte()
if err == io.EOF {
break
}
fmt.Print(string(c))
}
Its output is:
Reading.
And nothing happens. There is no new iteration, it is blocked. Now if you enter a line and press Enter, e.g. you enter Go!
, output will be:
Go!
GReading.
oReading.
!Reading.
Reading.
And again, waits for new input. As you can see, data is fed / available per line. This is what your terminal does: while you enter your line, it is not sent to os.Stdin
. Once you press Enter, the whole line is fed and is available from os.Stdin
. This is what we see: each letter of the input Go!
and a newline character. And we see the Reading.
text printed for each iteration. After the input is consumed, in.ReadByte()
is blocked again, waiting for new input. It does not report io.EOF
.
Now try the following: create a file e.g. a.txt
and edit it to have one line: Go!
and a newline. Now feed this file as the standard input to your program:
go run play.go < a.txt
Running it we'll see:
Reading.
GReading.
oReading.
!Reading.
Reading.
Reading.
And it terminates so it works! It works because this time the source of os.Stdin
is not your console / terminal, but the contents of a file, and once it's consumed, attempting to read from os.Stdin
will properly report io.EOF
.