2010-06-06 12 views
24

Próbowałem to:Haskell: natychmiast odczytać znak wejściowy z konsoli, a nie po nowej linii

main = do 
    hSetBuffering stdin NoBuffering 
    c <- getChar 

ale czeka, aż zostanie naciśnięty, który nie jest to, co chcę wejść. Chcę przeczytać postać natychmiast po naciśnięciu jej przez użytkownika.

Używam GHC v6.12.1 na Windows 7.

EDIT: obejście dla mnie było przejście od GHC do WinHugs, która obsługuje to poprawnie.

+2

To naprawdę nie jest dobrym obejście. Prawdziwym rozwiązaniem jest jawne wybieranie IO z buforowaniem znaków; za pomocą getch z systemu conio.h. Łącze Arteliusa zawiera przykładowy kod do tego. – jrockway

+0

To dobre rozwiązanie, jeśli mu odpowiada!Używanie innego narzędzia, które nie ma błędu, ma sens. Jeśli nie potrzebujesz funkcji językowych najnowszych kompilatorów ghc, WinHugs jest szybszy niż ghci czy winghci. Działa bez problemów i wygląda ładniej. Nie musisz też ": r", gdy edytujesz swój kod, który uwielbiam. – AndrewC

+0

Jak to się ma do 'main = do hSetBuffering stdin NoBuffering; współdziałać z $ map Data.Char.toUpper'? W moim przypadku czeka na nową linię, zanim pojawi się jakikolwiek wynik. (Ubuntu, GHC 7.6.3.) – ominug

Odpowiedz

18

Może być to błąd:

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

Poniższe powtórzenia programu Wprowadzane znaki aż klawisz Escape jest wciśnięty.

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 

Ponieważ linii hSetBuffering stdin NoBuffering to nie powinno być konieczne naciśnięcie klawisza enter pomiędzy klawiszy. Ten program działa poprawnie w WinHugs (od wersji 2006). Jednak GHC 6.8.2 nie powtarza znaków, dopóki nie zostanie naciśnięty klawisz Enter. Problem był reprodukowany ze wszystkich plików wykonywalnych GHC (ghci, ghc, runghc, runhaskell), używając zarówno cmd.exe i command.com w systemie Windows XP Professional ...

4

Hmm .. Właściwie nie widzę tego funkcja jest błędem. Kiedy czytasz stdin oznacza to, że chcesz pracować z "plikiem", a po włączeniu buforowania mówisz, że nie ma potrzeby buforowania odczytu. Ale to nie znaczy, że aplikacja emulująca ten "plik" nie powinna używać bufora zapisu. Dla Linuksa, jeśli twój terminal jest w trybie "icanon", nie wysyła żadnego wejścia, dopóki nie pojawi się jakieś specjalne zdarzenie (np. Enter wciśnięty lub Ctrl + D). Prawdopodobnie konsola w systemie Windows ma kilka podobnych trybów.

+0

Dziękuję. Brzmi zgodnie z prawdą, ale jeśli widzisz opis błędu: to naprawdę jest to, o co prosiłem, więc na razie zamierzam zaznaczyć odpowiedź Arteliusa. – Steves

+3

Przynajmniej działa inaczej w systemach Windows i Linux. Pod linuxem kod wysłany przez Steves działa BEZ czekania. Więc myślę, że to błąd i powinien zostać naprawiony. –

+0

Jest to (częściowo) niepoprawne. Podczas korzystania z oprogramowania do emulacji konsoli lub konsoli MinGW buforowanie jest nadal ignorowane. System Windows ignoruje buforowanie na poziomie systemu operacyjnego, a nie na poziomie konsoli. – YoYoYonnY

17

Tak, to błąd. Oto obejście, aby zapisać ludzie klikania i przewijania:

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

Więc można zastąpić wywołań getChar z połączeń do getHiddenChar.

Uwaga: jest to obejście tylko dla ghc/ghci w systemie Windows. Na przykład winhugs nie ma błędu i ten kod nie działa w winhugs.

+1

Podoba mi się to rozwiązanie. Zauważ jednak, że 'unsafe' spowoduje, że blokujące wywołanie' getch' zablokuje wszystkie inne wątki w programie. Jestem autorem pakietu ['hidden-char'] (http://hackage.haskell.org/package/hidden -char) w Hackage, który ma wariant tej funkcji, używając niebezpiecznego FFI. –

1

Pakiet dla mnie: Haskeline.

Jeśli potrzebujesz go dla pojedynczych znaków, to po prostu delikatnie zmień próbkę.

  1. getInputLine staje getInputChar
  2. "quit" staje 'q'
  3. ++ input staje ++ [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