1

I have text file containing data like that:

13.    
13.      
[(1,2),(2,3),(4,5)].

And I want to read this into 3 variables in Haskell. But standard functions read this as strings, but considering I get rid of dot at the end myself is there any built-in parser function that will make Integer of "13" and [(Integer,Integer)] list out of [(1,2),(2,3),(4,5)] ?

3 Answers 3

4

Yes, it's called read:

let i = read "13" :: Integer
let ts = read "[(1,2),(2,3),(4,5)]" :: [(Integer, Integer)]
Sign up to request clarification or add additional context in comments.

Comments

2

The example text file you gave has trailing spaces as well as the full stop, so merely cutting the last character doesn't work. Let's take just the digits, using:

import Data.Char (isDigit)

Why not have a data type to store the stuff from the file:

data MyStuff = MyStuff {firstNum :: Int,
                        secondNum:: Int,
                        intPairList :: [(Integer, Integer)]}
    deriving (Show,Read)

Now we need to read the file, and then turn it into individual lines:

getMyStuff :: FilePath -> IO MyStuff
getMyStuff filename = do
   rawdata <- readFile filename
   let [i1,i2,list] = lines rawdata
   return $ MyStuff  (read $ takeWhile isDigit i1)  (read $ takeWhile isDigit i2)  (read $ init list)

The read function works with any data type that has a Read instance, and automatically produces data of the right type.

> getMyStuff "data.txt" >>= print
MyStuff {firstNum = 13, secondNum = 13, intPairList = [(1,2),(2,3),(4,5)]}

A better way

I'd be inclined to save myself a fair bit of work, and just write that data directly, so

writeMyStuff :: FilePath -> MyStuff -> IO ()
writeMyStuff filename somedata = writeFile filename (show somedata)

readMyStuff :: FilePath -> IO MyStuff
readMyStuff filename = fmap read (readFile filename)

(The fmap just applies the pure function read to the output of the readFile.)

> writeMyStuff "test.txt" MyStuff {firstNum=12,secondNum=42, intPairList=[(1,2),(3,4)]}
> readMyStuff "test.txt" >>= print
MyStuff {firstNum = 12, secondNum = 42, intPairList = [(1,2),(3,4)]}

You're far less likely to make little parsing or printing errors if you let the compiler sort it all out for you, it's less code, and simpler.

2 Comments

Prelude.read no parse error. I have code like this: data EntryData = EntryData { height :: Integer, width :: Integer, entryList :: [(Integer, Integer)]} deriving (Show,Read) getEntryData :: FilePath -> IO EntryData getEntryData filename = do rawdata <- readFile filename let [h,w,l] = map init (lines rawdata) putStrLn h putStrLn w putStrLn l return $ EntryData (read h) (read w) (read l) main = do putStrLn "Enter filename: " fileName <- getLine getEntryData fileName >>= print Strings are correct :(
@user2349668 I'm on my phone at the moment, but are you sure the file contains the data, then a period, then the end of the line? If not, map init won't strip the trailling spaces. Test it with print instead of putStrLn to find out what otherwhitespace is inthe file.
2

Haskell's strong types require you to know what you're getting. So let's forgo all error checking and optimization and assume that the file is always in the right format, you can do something like this:

data Entry = Number Integer
           | List [(Integer, Integer)]

parseYourFile :: FilePath -> IO [Entry]
parseYourFile p = do
  content <- readFile p
  return $ parseYourFormat content

parseYourFormat :: String -> [Entry]
parseYourFormat data = map parseEntry $ lines data

parseEntry :: String -> Entry
parseEntry line = if head line == '['
                  then List $ read core
                  else Number $ read core
    where core = init line

Or you could write a proper parser for it using one of the many combinator frameworks.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.