I have the following C program:
#include <stdio.h>
#include <unistd.h>
void readAndEchoAll(void) {
for(;;) {
char buf[100];
ssize_t size = read(STDIN_FILENO, buf, sizeof(buf));
if(size <= 0) {
return;
}
fwrite(buf, 1, size, stdout);
}
}
int main(void) {
puts("Reading and echoing STDIN until first EOF...");
readAndEchoAll();
puts("Got first EOF. Now reading and echoing STDIN until second EOF...");
readAndEchoAll();
puts("Got second EOF.");
return 0;
}
When I run it, it works the way I want it to. Here's what it does:
Reading and echoing STDIN until first EOF...
asdf
^Dasdf
Got first EOF. Now reading and echoing STDIN until second EOF...
fdsa
^Dfdsa
Got second EOF.
I'm trying to create an equivalent Haskell program. Here's my attempt:
readAndEchoAll :: IO ()
readAndEchoAll = do
buf <- getContents
putStr buf
main :: IO ()
main = do
putStrLn "Reading and echoing STDIN until first EOF..."
readAndEchoAll
putStrLn "Got first EOF. Now reading and echoing STDIN until second EOF..."
-- ???
readAndEchoAll
putStrLn "Got second EOF."
This doesn't work. Here's what it does:
Reading and echoing STDIN until first EOF...
asdf
^Dasdf
Got first EOF. Now reading and echoing STDIN until second EOF...
readtwice.hs: <stdin>: hGetContents: illegal operation (handle is closed)
How do I make this work like the C program? I assume that I need to put some equivalent of clearerr(stdin);
where I have -- ???
, but I'm not sure what that equivalent is.
Update: Turns out clearerr
is a bit of a red herring, as it's exclusive to the standard C API. When using the POSIX API, you can just read again without needing to do anything equivalent to it. So rather than make Haskell do anything extra, I need to make it not do something: not prevent further reads once it sees EOF.
You can't use getContents
, because hGetContents
(semi-)closes the handle it's passed and getContents
calls hGetContents
. But there's no problem with reading from a handle again after EOF with most of the other functions from the standard library. Here's a simple but inefficient example of reading all the characters without using getContents
:
import Control.Exception
import System.IO.Error
readAll = go [] where
handler cs err = if isEOFError err
then return (reverse cs)
else throwIO err
go cs = catch (do
c <- getChar
go (c:cs))
(handler cs)
main = do
all <- readAll
putStrLn $ "got: " ++ all
putStrLn "go again, mate"
all <- readAll
putStrLn $ "got: " ++ all
If you want better efficiency, there are various functions available for reading lines-at-a-time or other large chunks in the standard library, rather than one character at a time.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With