I've tried this:
main = do
hSetBuffering stdin NoBuffering
c <- getChar
but it waits until the enter is pressed, which is not what I want. I want to read the character immediately after user presses it.
I am using ghc v6.12.1 on Windows 7.
EDIT: workaround for me was moving from GHC to WinHugs, which supports 开发者_如何学Cthis correctly.
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.
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...
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.
The Haskeline package worked for me.
If you need it for individual characters, then just change the sample slightly.
getInputLine
becomesgetInputChar
"quit"
becomes'q'
++ 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
From comment of @Richard Cook:
Use hidden-char: Provides cross-platform getHiddenChar function.
I used the haskeline package, suggested in other answers, to put together this simple alternative to getChar
. It requests input again in the case that getInputChar
returns Nothing
. This worked for me to get past the issue; modify as needed.
import System.Console.Haskeline
( runInputT
, defaultSettings
, getInputChar
)
betterInputChar :: IO Char
betterInputChar = do
mc <- runInputT defaultSettings (getInputChar "")
case mc of
Nothing -> betterInputChar
(Just c) -> return c
精彩评论