如何从标准输入流(stdin)逐字节读取数据?

huangapple go评论104阅读模式
英文:

How to read bytewise from stdin?

问题

in, out := bufio.NewReader(os.Stdin), bufio.NewWriter(os.Stdout)
for {
c, err := in.ReadByte()
if err == io.EOF {
break
}
out.WriteByte(c)
}

我想从标准输入流逐字节读取。与Read方法不同,ReadByte似乎不会返回io.EOF。如果所有字节都已读取,我该如何跳出循环?

英文:
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?

答案1

得分: 2

这不是Reader.ReadByte()实现的问题,也不是bufio.NewReader()的问题。

看看这个例子来证明:

buf := bytes.NewBufferString("Hello World!\n")
in := bufio.NewReader(buf)
for {
    c, err := in.ReadByte()
    if err == io.EOF {
        break
    }
    fmt.Print(string(c))
}

运行上述代码,会输出:

Hello World!

并且正常终止。

你的问题出在os.Stdin上。从它读取是特定于其来源的。如果它是你的终端,从中读取会简单地阻塞而不报告io.EOF。看看这个例子来证明:

in := bufio.NewReader(os.Stdin)
for {
    fmt.Println("Reading.")
    c, err := in.ReadByte()
    if err == io.EOF {
        break
    }
    fmt.Print(string(c))
}

它的输出是:

Reading.

然后什么都不会发生。没有新的迭代,它被阻塞了。现在,如果你输入一行并按下<kbd>Enter</kbd>,例如输入Go!,输出将是:

Go!
GReading.
oReading.
!Reading.

Reading.

然后再次等待新的输入。正如你所看到的,数据是按行提供/可用的。这就是你的终端的工作方式:当你输入一行时,它不会发送到os.Stdin。一旦你按下<kbd>Enter</kbd>,整行就被提供并可以从os.Stdin中获取。这就是我们看到的:输入Go!的每个字母和一个换行符。我们看到Reading.文本在每次迭代中打印。在输入被消耗完之后,in.ReadByte()再次被阻塞,等待新的输入。它不会报告io.EOF

现在试试以下操作:创建一个文件,例如a.txt,并将其编辑为一行:Go!和一个换行符。现在将此文件作为标准输入传递给你的程序:

go run play.go < a.txt

运行它,我们会看到:

Reading.
GReading.
oReading.
!Reading.

Reading.

Reading.

然后它终止了,所以它正常工作!它之所以正常工作,是因为这次os.Stdin的来源不是你的控制台/终端,而是文件的内容,一旦它被消耗完,尝试从os.Stdin读取将正确报告io.EOF

英文:

This is not an issue of the Reader.ReadByte() implementation, nor that of bufio.NewReader().

See this example to prove it:

buf := bytes.NewBufferString(&quot;Hello World!\n&quot;)
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(&quot;Reading.&quot;)
	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 <kbd>Enter</kbd>, 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 <kbd>Enter</kbd>, 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 &lt; 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.

huangapple
  • 本文由 发表于 2017年8月23日 16:29:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/45834542.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定