如何处理Haskell中的半关闭句柄错误?

5

我在GHCI中遇到了以下错误:

*** Exception: <stdin>: hGetLine: illegal operation (handle is semi-closed)

这是在运行以下代码之后出现的:

main = do
    interact $ unlines . fmap proccess . take x . lines
    readLn :: IO Int

我相信问题的原因是take x。使用interact读取 x 行输入是否有更好的方法,还是interact只能单打独斗?


interact 将标准输入置于半关闭状态。我假设您的函数第一次运行时已经正确执行,但之后的每次调用都失败了?请关闭并重新打开 GHCi 或编译您的程序并执行它。 - R B
准确地说,每次对stdin的读取之后都失败了。 - Ford O.
顺便说一下,除了关闭并重新打开ghci,是否有其他方法可以重新打开已关闭的stdin句柄? - Ford O.
1
正如@RowanBlush所指出的那样,stdin句柄会被interact或任何读取整个stdin的函数置于半关闭状态。请记住,您的GHCI会话基本上是一个大的do块 - 编写main = getContents >> getContents同样无效。如果您想要读取n行,请使用replicateM n getLine{readLn/whatever} - 使用一个读取所有stdin的函数将始终在之后关闭stdin。您无法“重新打开”stdin - 它只是底层的Unix文件描述符。 - user2407038
2个回答

8
使用 interact 是无法实现你想要的目标的。在幕后,interact 使用hGetContents方法占据了整个 stdin。这将句柄置于“半关闭”状态,除了关闭之外,您无法再尝试与句柄进行任何交互,因为它的所有输入已被消耗(惰性)。

尝试使用以下代码读取有限行数 -

import Control.Monad (replicateM)

getLines :: Int -> IO [String]
getLines n = replicateM n getLine

2

概念上,interact 函数会消耗 全部 标准输入。因此,在调用 readLn 之后再使用它是没有意义的。

如果你只想读取指定行数,可以使用以下代码:

import Control.Monad

main = do input <- replicateM 10 getLine
          ...

这里的input将是一个包含10个字符串(确切数量)的列表。

如果您希望允许更少的行或在遇到特殊条件时停止读取,则情况会变得更加复杂。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接