2010-06-06 9 views
24

Ich habe diese versucht:Haskell: Eingabezeichen von der Konsole sofort lesen, nicht nach Newline

main = do 
    hSetBuffering stdin NoBuffering 
    c <- getChar 

aber es wartet, bis die Eingabe gedrückt wird, was nicht das, was ich will. Ich möchte das Zeichen sofort lesen, nachdem der Benutzer es gedrückt hat.

Ich verwende ghc v6.12.1 auf Windows 7.

EDIT: Abhilfe für mich von GHC zu WinHugs bewegte, was dies richtig unterstützt.

+2

wird wird, dass eine gute Abhilfe nicht wirklich ist. Die tatsächliche Problemumgehung besteht darin, explizit zwischen Zeichen gepufferte EA auszuwählen; mit dem getch aus dem System conio.h. Der Link von Artelius enthält Beispielcode dafür. – jrockway

+0

Es ist ein guter Workaround, wenn es ihm passt!Es ist sinnvoll, ein anderes Tool zu verwenden, das den Fehler nicht enthält. Wenn Sie die Sprachfunktionen der letzten GhC-Compiler nicht benötigen, ist WinHugs meiner Erfahrung nach schneller als ghci oder winghci. Es funktioniert ohne viel Aufhebens und sieht schöner aus. Sie müssen auch nicht ': r', wenn Sie Ihren Code bearbeitet haben, die ich liebe. – AndrewC

+0

Wie verhält es sich mit 'main = do hSetBuffering stdin NoBuffering; interagieren $ map Data.Char.toUpper'? In meinem Fall wartet es auf eine neue Zeile, bevor eine Ausgabe erscheint. (Ubuntu, GHC 7.6.3.) – ominug

Antwort

18

könnte ein Fehler sein:

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

Folgende Programmwiederholungen eingegebenen Zeichen, bis die Escape-Taste gedrückt wird.

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 

Wegen der hSetBuffering stdin NoBuffering Linie ist es nicht notwendig sein sollte, die Enter-Taste zwischen den Tastenanschlägen zu drücken. Dieses Programm funktioniert korrekt in WinHugs (sep 2006 Version). GHC 6.8.2 wiederholt jedoch die Zeichen nicht, bis die Eingabetaste gedrückt wird. Das Problem wurde mit allen GHC ausführbaren Dateien wiedergegeben (GHCI, ghc, runghc, runhaskell), die beide cmd.exe mit und command.com unter Windows XP Professional ...

4

Hmm .. Eigentlich kann ich das nicht sehen Feature ein Fehler sein. Wenn Sie stdin lesen, bedeutet das, dass Sie mit einer "Datei" arbeiten möchten, und wenn Sie die Pufferung ausschalten, sagen Sie, dass kein Lesepuffer benötigt wird. Aber das bedeutet nicht, dass die Anwendung, die diese "Datei" emuliert, keinen Schreibpuffer verwenden sollte. Für Linux, wenn Ihr Terminal im "icanon" -Modus ist, sendet es keine Eingabe, bis ein spezielles Ereignis eintritt (wie Eingabe gedrückt oder Strg + D). Wahrscheinlich Konsole in Windows haben einige ähnliche Modi.

+0

Vielen Dank. Klingt ehrlich, aber wenn du die Fehlerbeschreibung siehst: es ist wirklich genau das, was ich gefragt habe, also werde ich jetzt die Antwort von Artelius markieren. – Steves

+3

Zumindest funktioniert es unter Windows und Linux anders. Unter Linux funktioniert der von Steves gepostete Code OHNE zu warten. Also ich denke es ist ein Fehler und sollte behoben werden. –

+0

Dies ist (teilweise) falsch. Bei Verwendung der Konsolenemulationssoftware oder der MinGW-Konsole wird die Pufferung weiterhin ignoriert. Windows ignoriert die Pufferung auf Betriebssystemebene, nicht auf Konsolenebene. – YoYoYonnY

17

Ja, es ist ein Fehler. Hier ist eine Abhilfe Leute speichern Klicken und Scrollen:

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

So können Sie Anrufe auf getChar mit Aufrufen an getHiddenChar ersetzen.

Beachten Sie, dass dies eine Problemumgehung nur für ghc/ghci unter Windows ist. Zum Beispiel hat winhugs den Fehler nicht und dieser Code funktioniert nicht in winhugs.

+1

Ich mag diese Lösung. Beachten Sie jedoch, dass 'unsicher 'einen blockierenden Aufruf von' getch' verursacht, um alle anderen Threads im Programm zu blockieren. Ich bin Autor des Pakets "hidden-char" (http://hackage.haskell.org/package/hidden-char) auf Hackage, das eine Variante dieser Funktion unter Verwendung des unsicheren FFI hat. –

1

Das Haskeline Paket funktionierte für mich.

Wenn Sie es für einzelne Zeichen benötigen, dann ändern Sie einfach die Probe leicht.

  1. getInputLine wird 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