问题
Here is minimal complete example:
import Control.Monad
import System.IO
loop :: IO ()
loop =
do line <- getLine
putStrLn line
eof <- isEOF
unless eof loop
main = loop
This program is supposed to read a line, print it out, stop if there is 'end of file' character in stdin
. It doesn't leave the loop at all.
If I put eof <- isEOF
before putStrLn line
the program behaves very strange (try it!). I cannot get it at all: how putStrLn
can possibly affect input stream and why doesn't the program terminate when I put 'end of file' character into stream (with Ctrl+D)?
Description of program's behavior when eof <- isEOF
goes before putStrLn line
:
After entering of a line, program does not print the entered line, but expects more input. As it gets more input, it starts to print previously entered lines. This is log of a test:
foo
boo
output: foo
bar
output: boo
baz
output: bar
< here I press Ctrl-D >
output: baz
Source:
import Control.Monad
import System.IO
loop :: IO ()
loop =
do line <- getLine
eof <- isEOF
putStrLn $ "output: " ++ line
unless eof loop
main =
do hSetBuffering stdin LineBuffering
loop
回答1:
From http://lambda.haskell.org/platform/doc/current/ghc-doc/libraries/haskell2010-1.1.1.0/System-IO.html#g:11:
NOTE: hIsEOF may block, because it has to attempt to read from the stream to determine whether there is any more data to be read.
The putStrLn
doesn't affect the isEOF
, but the isEOF
prevents the program from getting to the putStrLn
before more characters are available, or you have actually pressed ^D
.
So you should never use hIsEOF
/isEOF
until the point in the program where you are ready to read more characters if there are any.
来源:https://stackoverflow.com/questions/26507642/why-iseof-doesnt-work