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 пакет работал для меня.

Если вам это нужно для отдельных символов, то просто изменить образец немного.

  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