PHPFixing
  • Privacy Policy
  • TOS
  • Ask Question
  • Contact Us
  • Home
  • PHP
  • Programming
  • SQL Injection
  • Web3.0

Sunday, October 30, 2022

[FIXED] How do I read from standard input again after an EOF?

 October 30, 2022     c, eof, haskell, io-monad, stdin     No comments   

Issue

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.


Solution

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.



Answered By - Daniel Wagner
Answer Checked By - David Marino (PHPFixing Volunteer)
  • Share This:  
  •  Facebook
  •  Twitter
  •  Stumble
  •  Digg
Newer Post Older Post Home

0 Comments:

Post a Comment

Note: Only a member of this blog may post a comment.

Total Pageviews

Featured Post

Why Learn PHP Programming

Why Learn PHP Programming A widely-used open source scripting language PHP is one of the most popular programming languages in the world. It...

Subscribe To

Posts
Atom
Posts
Comments
Atom
Comments

Copyright © PHPFixing