2010-06-06 14 views
24

Ho provato questo:Haskell: leggere carattere input da console subito, non dopo newline

main = do 
    hSetBuffering stdin NoBuffering 
    c <- getChar 

ma attende fino a quando la si preme, che non è quello che voglio entrare. Voglio leggere il personaggio immediatamente dopo che l'utente lo ha premuto.

Sto usando v6.12.1 ghc su Windows 7.

EDIT: soluzione per me muovevo dalla GHC a WinHugs, che supporta questo modo corretto.

+2

che non è proprio una buona soluzione. La vera soluzione è scegliere in modo esplicito IO con buffer dei caratteri; usando il getch dal sistema conio.h. Il link di Artelius contiene un codice di esempio per questo. – jrockway

+0

È una buona soluzione se gli si addice!Utilizzare uno strumento diverso che non ha il bug ha senso. Se non hai bisogno delle funzionalità linguistiche dei recenti compilatori ghc, WinHugs è più veloce di ghci o winghci nella mia esperienza. Funziona senza problemi e sembra più bello. Inoltre non è necessario ': r' quando hai modificato il tuo codice, che adoro. – AndrewC

+0

In che modo si riferisce a 'main = do hSetBuffering stdin NoBuffering; interagisci $ map Data.Char.toUpper'? Nel mio caso attende una nuova riga prima che appaia qualsiasi uscita. (Ubuntu, GHC 7.6.3.) – ominug

risposta

18

potrebbe essere un bug:

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

I seguenti ripete programma caratteri immessi fino a quando si preme il tasto 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 

causa della linea stdin NoBuffering hSetBuffering non dovrebbe essere necessario premere il tasto invio tra tasti. Questo programma funziona correttamente in WinHugs (versione settembre 2006). Tuttavia, GHC 6.8.2 non ripete i caratteri finché non viene premuto il tasto Invio. Il problema è stato riprodotto con tutti i file eseguibili GHC (ghci, GHC, runghc, runhaskell), utilizzando sia cmd.exe e command.com su Windows XP Professional ...

4

Hmm .. In realtà non riesco a vedere questo caratteristica di essere un bug. Quando si legge stdin ciò significa che si desidera lavorare con un "file" e quando si disabilita il buffering si sta dicendo che non è necessario il buffer di lettura. Ma ciò non significa che l'applicazione che emula quel "file" non dovrebbe usare il buffer di scrittura. Per Linux se il tuo terminale è in modalità "icanon" non invierà alcun input fino a quando non si verificherà qualche evento speciale (come Invio premuto o Ctrl + D). Probabilmente la console di Windows ha alcune modalità simili.

+0

Grazie. Sembra sincero, ma se vedi la descrizione del bug: è davvero esatto quello che stavo chiedendo, quindi per ora segnerò la risposta di Artelius. – Steves

+3

Almeno funziona diversamente su Windows e Linux. Sotto Linux il codice inviato da Steves funziona SENZA aspettare. Quindi penso che sia un bug, e dovrebbe essere risolto. –

+0

Questo è (parzialmente) errato. Quando si utilizza Console Emulation Software o la console MinGW, il buffering viene ancora ignorato. Windows ignora il buffering a livello di sistema operativo, non a livello di console. – YoYoYonnY

17

Sì, è un bug. Ecco una soluzione per salvare la gente clic e scorrimento:

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

modo da poter sostituire le chiamate al getChar con chiamate a getHiddenChar.

Nota questa è una soluzione alternativa solo per ghc/ghci su Windows. Ad esempio, winhugs non ha il bug e questo codice non funziona in winhugs.

+1

Mi piace questa soluzione. Si noti, tuttavia, che 'unssafe 'causerà una chiamata di blocco a' getch' per bloccare tutti gli altri thread nel programma. Sono autore del pacchetto ['hidden-char'] (http://hackage.haskell.org/package/hidden-char) su Hackage che ha una variante di questa funzione che utilizza l'FFI non sicuro. –

1

Il pacchetto Haskeline ha funzionato per me.

Se è necessario per singoli caratteri, è sufficiente modificare leggermente il campione.

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