Как я могу читать со стандартного ввода снова после EOF? - PullRequest
5 голосов
/ 20 мая 2019

У меня есть следующая программа на 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 ]

5 голосов
/ 20 мая 2019

Вы не можете использовать 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

Если вы хотите повысить эффективность, существуют различные функции, доступные для чтения строк за раз или других больших кусковв стандартной библиотеке, а не по одному символу за раз.

0 голосов
/ 20 мая 2019

Быстрый поиск исходного кода GHC показывает, что clearerr() там вообще не используется.Однако вы можете снова открыть /dev/stdin, так как похоже, что вы используете Linux или подобное.Попробуйте это:

stdin2 <- openFile "/dev/stdin" ReadMode

Вы также можете использовать hDuplicate.Смотрите здесь: Портативно открывая ручку для многократного ввода за один раз

...