EOF后如何再次从标准输入中读取内容?

时间:2019-05-20 13:04:21

标签: c haskell stdin eof io-monad

我有以下C程序:

#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;
}

当我运行它时,它会按照我想要的方式工作。这是它的作用:

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.

我正在尝试创建一个等效的Haskell程序。这是我的尝试:

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."

这不起作用。这是它的作用:

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)

如何使它像C程序一样工作? 我假设我需要在有clearerr(stdin);的地方放上-- ???的等价物,但是我不确定那是什么。

更新:事实证明clearerr有点让人讨厌,因为它是标准C API所独有的。使用POSIX API时,您无需做任何等同的工作即可再次阅读。因此,除了让Haskell做任何额外的事情外,我还需要使它不做任何事情:一旦看到EOF,就不要阻止进一步的读取。

2 个答案:

答案 0 :(得分:5)

您不能使用getContents,因为hGetContents(半)关闭了它传递的句柄,并且getContents调用hGetContents。但是,在EOF之后使用标准库中的大多数其他功能再次从句柄读取没有问题。这是一个简单但效率低下的示例,无需使用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

如果要提高效率,可以使用多种功能来一次读取一行或标准库中的其他大块数据,而不是一次读取一个字符。

答案 1 :(得分:0)

快速搜索GHC源代码表明clearerr()根本没有使用。但是,由于您似乎正在使用Linux或类似操作系统,因此可以再次打开/dev/stdin。试试这个:

stdin2 <- openFile "/dev/stdin" ReadMode

您也可以使用hDuplicate。看到这里:Portably opening a handle to stdin many times in a single session