I would like to have a getInt :: IO Int
function on Haskell. It would be a function that would take an integer from stdin, leaving the rest of the buffer intact.
The library function with this type I have found, readLn :: IO Int
, for example, would not work with an input like:
2
3 4
Because it would take the entire line 3 4
, instead of taking 3
and leaving 4
for the next getInt
. Although I know I could read the complete string and then split it using words
, I would like to ask if there is a way which does not consume the buffer.
Is there any function on the standard libraries doing this? Is there any simple/obvious way to create a getInt
I am missing?
You could write such a function using hLookAhead
and catching isEOFError
. However, you'll be mixing your IO with your parsing.
A more Haskellish solution is to define:
parseInts :: String -> [Int]
parseInts str = map read (words str)
and then use parseInts
like this:
-- read an entire file and convert to a list of Ints
nums <- fmap parseInts getContents
-- read just a line and convert to a list of Ints
nums <- fmap parseInts getLine
To parse a file which looks like:
3 4
1 1 1 1
2 2 2 2
3 3 3 3
(e.g. 3 = number of rows, 4 = number of columns), you have a bunch of options:
import Control.Monad (replicateM)
main = do
(nrows : ncols : _) <- fmap parseInts getLine
rows <- replicateM nrows $ fmap parseInts getLine
or
import Data.List.Split (chunksOf)
main = do
(nrows : ncols : rest) <- fmap parseInts getContents
let rows = chunksOf ncols rest
or even:
main = do
(nrows : ncols : _) <- fmap parseInts getLine
forM_ [1..nrows] $ \i -> do
row <- fmap parseInts getLine
... do something with row...
Here I turn my comment into an answer, since it seems the original asker is okay with consuming separators. First we will define a getWord
analog to getLine
; then we will readIO
the result. We will be careful to catch EOF exceptions in case they occur.
import Control.Exception
import Control.Applicative
import Data.Char
import System.IO.Error
getWord :: IO String
getWord = handle handleEOF $ do
c <- getChar
if isSpace c then return [] else (c:) <$> getWord
where
handleEOF e = if isEOFError e then return [] else throwIO e
readWord :: Read a => IO a
readWord = getWord >>= readIO
Beware: this will of course not work well for values to be read that have a space in them (like Data.Map
s or other complex data types).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With