【问题标题】:Haskell: read input character from console immediately, not after newlineHaskell:立即从控制台读取输入字符,而不是在换行符之后
【发布时间】:2011-02-28 08:44:35
【问题描述】:

我试过了:

main = do
    hSetBuffering stdin NoBuffering 
    c <- getChar

但它会等到按下回车键,这不是我想要的。我想在用户按下后立即读取字符。

我在 Windows 7 上使用 ghc v6.12.1。

编辑:对我来说,解决方法是从 GHC 转移到 WinHugs,它正确支持这一点。

【问题讨论】:

  • 这不是一个很好的解决方法。真正的解决方法是显式选择字符缓冲 IO;通过使用系统 conio.h 中的 getch。 Artelius 的链接包含示例代码。
  • 如果适合他,这是一个很好的解决方法!使用没有错误的不同工具是有意义的。如果您不需要最近 ghc 编译器的语言功能,根据我的经验,WinHugs 比 ghci 或winghci 更快。它可以毫不费力地工作,看起来更好。当您编辑代码时,您也不需要:r,我很喜欢。
  • 它与main = do hSetBuffering stdin NoBuffering; interact $ map Data.Char.toUpper 有什么关系?在我的情况下,它会在任何输出出现之前等待一个新行。 (Ubuntu,GHC 7.6.3。)

标签: windows haskell io ghc buffering


【解决方案1】:

是的,这是一个错误。这里有一个解决方法来节省人们点击和滚动:

{-# LANGUAGE ForeignFunctionInterface #-}
import Data.Char
import Foreign.C.Types
getHiddenChar = fmap (chr.fromEnum) c_getch
foreign import ccall unsafe "conio.h getch"
  c_getch :: IO CInt

因此,您可以将调用 getChar 替换为调用 getHiddenChar

请注意,这仅适用于 Windows 上的 ghc/ghci。比如winhugs就没有这个bug,这个代码在winhugs里就不行。

【讨论】:

  • 我喜欢这个解决方案。但是请注意,unsafe 将导致对getch 的阻塞调用以阻塞程序中的所有其他线程。我是 Hackage 上 hidden-char 软件包的作者,该软件包有一个使用不安全 FFI 的函数变体。
  • 如果你在 GHCi 中得到类似于 ByteCodeLink: can't find label During interactive linking, GHCi couldn't find the following symbol: getch 的内容,请尝试使用 _getch 而不是 getch 进行外部导入。来源:stackoverrun.com/de/q/10551494
【解决方案2】:

可能是一个错误:

http://hackage.haskell.org/trac/ghc/ticket/2189

以下程序重复输入的字符,直到按下转义键。

import IO
import Monad
import Char

main :: IO ()
main = do hSetBuffering stdin NoBuffering
          inputLoop

inputLoop :: IO ()
inputLoop = do i <- getContents
               mapM_ putChar $ takeWhile ((/= 27) . ord) i

由于 hSetBuffering 标准输入 NoBuffering 行,因此不必在击键之间按回车键。该程序在 WinHugs(2006 年 9 月版)中正常运行。但是,GHC 6.8.2 在按下回车键之前不会重复字符。在 Windows XP Professional 上使用 cmd.exe 和 command.com 时,所有 GHC 可执行文件(ghci、ghc、runghc、runhaskell)都重现了该问题...

【讨论】:

    【解决方案3】:

    嗯.. 实际上我看不出这个功能是一个错误。当您阅读stdin 时,这意味着您想要使用“文件”,而当您关闭缓冲时,您表示不需要读取缓冲区。但这并不意味着模拟该“文件”的应用程序不应该使用写缓冲区。对于 linux,如果您的终端处于“icanon”模式,它不会发送任何输入,直到发生某些特殊事件(如按下 Enter 或 Ctrl+D)。可能 Windows 中的控制台也有一些类似的模式。

    【讨论】:

    • 谢谢。听起来很真实,但是如果您看到错误描述:这确实是我所要求的,所以现在我要标记 Artelius 的答案。
    • 至少它在 Windows 和 Linux 下的工作方式不同。在 linux 下,Steves 发布的代码无需等待即可工作。所以我认为这是一个错误,应该修复。
    • 这是(部分)不正确的。使用控制台仿真软件或 MinGW 控制台时,仍然会忽略缓冲。 Windows 会忽略操作系统级别的缓冲,而不是控制台级别的缓冲。
    【解决方案4】:

    Haskeline 包对我有用。

    如果您需要单个字符,则只需稍微更改示例即可。

    1. getInputLine 变为 getInputChar
    2. "quit" 变为 'q'
    3. ++ input 变为 ++ [input]
    main = runInputT defaultSettings loop
        where 
            loop :: InputT IO ()
            loop = do
                minput <- getInputChar "% "
                case minput of
                    Nothing -> return ()
                    Just 'q' -> return ()
                    Just input -> do outputStrLn $ "Input was: " ++ [input]
                                     loop
    

    【讨论】:

      【解决方案5】:
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-10-17
      • 1970-01-01
      • 2011-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多