Haskell: считывание входного символа с консоли немедленно, а не после новой строки
Я попытался это:
main = do
hSetBuffering stdin NoBuffering
c <- getChar
но он ждет, пока не будет нажата enter, что не то, что я хочу. Я хочу прочитать символ сразу после того, как пользователь нажмет на него.
Я использую ghc v6.12.1 в Windows 7.
EDIT: обходной путь для меня переходил от GHC к WinHugs, который поддерживает это правильно.
4 ответов
может быть ошибка:
http://hackage.haskell.org/trac/ghc/ticket/2189
следующая программа повторяет введенные символы до нажатия клавиши escape.
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 stdin NoBuffering не должно быть необходимости нажимать клавишу enter между нажатиями клавиш. Эта программа работает правильно в WinHugs (версия sep 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