Haskell: read input character from console immediately, not after newline

前端 未结 4 412
暗喜
暗喜 2020-12-09 08:17

I\'ve tried this:

main = do
    hSetBuffering stdin NoBuffering 
    c <- getChar

but it waits until the enter is pressed, which is not

相关标签:
4条回答
  • 2020-12-09 08:34

    Might be a bug:

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

    The following program repeats inputted characters until the escape key is pressed.

    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
    

    Because of the hSetBuffering stdin NoBuffering line it should not be necessary to press the enter key between keystrokes. This program works correctly in WinHugs (sep 2006 version). However, GHC 6.8.2 does not repeat the characters until the enter key is pressed. The problem was reproduced with all GHC executables (ghci, ghc, runghc, runhaskell), using both cmd.exe and command.com on Windows XP Professional...

    0 讨论(0)
  • 2020-12-09 08:35

    Yes, it's a bug. Here's a workaround to save folks clicking and scrolling:

    {-# 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
    

    So you can replace calls to getChar with calls to getHiddenChar.

    Note this is a workaround just for ghc/ghci on Windows. For example, winhugs doesn't have the bug and this code doesn't work in winhugs.

    0 讨论(0)
  • 2020-12-09 08:37

    Hmm.. Actually I can't see this feature to be a bug. When you read stdin that means that you want to work with a "file" and when you turn of buffering you are saying that there is no need for read buffer. But that doesn't mean that application which is emulating that "file" should not use write buffer. For linux if your terminal is in "icanon" mode it doesn't send any input until some special event will occur (like Enter pressed or Ctrl+D). Probably console in Windows have some similar modes.

    0 讨论(0)
  • 2020-12-09 08:56

    The Haskeline package worked for me.

    If you need it for individual characters, then just change the sample slightly.

    1. getInputLine becomes getInputChar
    2. "quit" becomes 'q'
    3. ++ input becomes ++ [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
    
    0 讨论(0)
提交回复
热议问题