Haskell: читать вводимый символ с консоли сразу, а не после новой строки - PullRequest
26 голосов
/ 06 июня 2010

Я пробовал это:

main = do
    hSetBuffering stdin NoBuffering 
    c <- getChar

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

Я использую ghc v6.12.1 в Windows 7.

РЕДАКТИРОВАТЬ: обходной путь для меня был переход с GHC на WinHugs, который поддерживает это правильно.

Ответы [ 4 ]

20 голосов
/ 14 ноября 2012

Да, это ошибка. Вот обходной путь, чтобы сохранить людей, щелкающих и прокручивающих:

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

20 голосов
/ 06 июня 2010

Может быть ошибка:

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

Следующая программа повторяет введенные символы до нажатия клавиши ESC.

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 ...

3 голосов
/ 06 июня 2010

Хм .. На самом деле я не вижу эту функцию, чтобы быть ошибкой.Когда вы читаете stdin, это означает, что вы хотите работать с «файлом», и когда вы включаете буферизацию, вы говорите, что нет необходимости в буфере чтения.Но это не означает, что приложение, эмулирующее этот «файл», не должно использовать буфер записи.Для linux, если ваш терминал находится в режиме «icanon», он не отправляет никаких входных данных, пока не произойдет какое-либо специальное событие (например, нажатие клавиши Enter или Ctrl + D).Вероятно, консоль в Windows имеет несколько похожих режимов.

2 голосов
/ 25 сентября 2013

Пакет 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
...