Haskell: читать вводимый символ с консоли сразу, а не после новой строки
Я пробовал это:
main = do
hSetBuffering stdin NoBuffering
c <- getChar
но он ждет, пока не будет нажата кнопка ввода, а это не то, что я хочу. Я хочу прочитать символ сразу после нажатия пользователем.
Я использую GHC v6.12.1 на Windows 7.
РЕДАКТИРОВАТЬ: обходной путь для меня был переход с GHC на WinHugs, который поддерживает это правильно.
6 ответов
Может быть ошибка:
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
Из-за строки NoBuffering stDin hSetBuffering не должно быть необходимости нажимать клавишу ввода между нажатиями клавиш. Эта программа корректно работает в WinHugs (сентябрь 2006 г.). Однако GHC 6.8.2 не повторяет символы до тех пор, пока не будет нажата клавиша ввода. Эта проблема была воспроизведена со всеми исполняемыми файлами GHC (ghci, ghc, runghc, runhaskell) с использованием cmd.exe и command.com в Windows XP Professional...
Да, это ошибка. Вот обходной путь, чтобы сохранить людей, щелкающих и прокручивающих:
{-# 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
,
Обратите внимание, что это обходной путь только для ghc / ghci в Windows. Например, в winhugs нет ошибки, и этот код не работает в winhugs.
Хм.. На самом деле я не вижу эту функцию, чтобы быть ошибкой. Когда ты читаешь stdin
это означает, что вы хотите работать с "файлом", и когда вы включаете буферизацию, вы говорите, что нет необходимости в буфере чтения. Но это не означает, что приложение, эмулирующее этот "файл", не должно использовать буфер записи. Для linux, если ваш терминал находится в режиме "icanon", он не отправляет никаких входных данных, пока не произойдет какое-то специальное событие (например, нажатие клавиши Enter или Ctrl+D). Вероятно, консоль в Windows имеет несколько похожих режимов.
Пакет Haskeline работал для меня.
Если вам это нужно для отдельных символов, просто измените образец немного.
getInputLine
становитсяgetInputChar
"quit"
становится'q'
++ 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
Я использовал пакет haskeline , предложенный в других ответах, чтобы собрать эту простую альтернативуgetChar
. Он снова запрашивает ввод в случае, еслиgetInputChar
возвращаетсяNothing
. Это помогло мне решить проблему; модифицировать по мере необходимости.
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