I was trying to get familiar with System.IO and handles. I want to just read a csv file, put a line of comma separated integers into a list, put that list into a list of lists, then put the next line into the list of lists, and so forth until the end of the file. I can read the file, and read a line of the file, but am struggling to make something that will do what I'm trying to do. The csv file, "1.csv" looks like this:
1,2,3,4
4,3,2,1
12,13
The code I have so far is as follows. It finds an error at the line "_ -> x:xs". I'm not used to using case x of at all so I might not be using that right, but it seems like a way to go with this. I know a lot of what I'm doing is wrong, but I'm not really sure how to go further.
import System.IO
xs = []
xss = []
readTheLine = do handle <- openFile "1.csv" ReadMode
x <- hGetChar handle
case x of
'\n' -> xs:xss
_ -> x:xs
'' -> putStrLn xss
main = readTheLine
I'm also pretty sure '' wouldn't signal the end of the file, but not sure what to do here and was trying to show what I'm going for.
Related
Hello i am having problems reading after saving and appending a List of Tuple Lists inside a File.
Saving something into a File works without problems.
I am saving into a file with
import qualified Data.ByteString as BS
import qualified Data.Serialize as S (decode, encode)
import Data.Either
toFile path = do
let a = take 1000 [100..] :: [Float]
let b = take 100 [1..] :: [Float]
BS.appendFile path $ S.encode (a,b)
and reading with
fromFile path = do
bstr<-BS.readFile path
let d = S.decode bstr :: Either String ([Float],[Float])
return (Right d)
but reading from that file with fromFileonly gives me 1 Element of it although i append to that file multiple times.
Since im appending to the file it should have multiple Elements inside it so im missing something like map on my fromFile function but i couldnt work out how.
I appreciate any help or any other solutions so using Data.Serialize and ByteString is not a must. Other possibilities i thought of are json files with Data.Aeson if i cant get it to work with Serialize
Edit :
I realized that i made a mistake on the decoding type in fromFile
let d = S.decode bstr :: Either String ([Float],[Float])
it should be like this
let d = S.decode bstr :: Either String [([Float],[Float])]
The Problem In Brief The default format used by serialize (or binary) encoding isn't trivially append-able.
The Problem (Longer)
You say you appended:
S.encode (a,b)
to the same file "multiple times". So the format of the file is now:
[ 64 bit length field | # floats encoded | 64 length field | # floats encoded ]
Repeated however many times you appended to the file. That is, each append will add new length fields and list of floats while leaving the old values in place.
After that you returned to read the file and decode some floats using, morally, S.decode <$> BS.readFile path. This will decode the first two lists of floats by first reading the length field (of the first time you wrote to the file) then the following floats and the second length field followed by its related floats. After reading the stated length worth of floats the decoder will stop.
It should now be clear that just because you appended more data does not make your encoding or decoding script look for any additional data. The default format used by serialize (or binary) encoding isn't trivially append-able.
Solutions
You mentioned switching to Aeson, but using JSON to encode instead of binary won't help you. Decoding two appended JSON strings like { "first": [1], "second": [2]}{ "first": [3], "second": [4]} is logically the same as your current problem. You have some unknown number of interleaved chunks of lists - just write a decoder to keep trying:
import Data.Serialize as S
import Data.Serialize.Get as S
import Data.ByteString as BS
fromFile path = do
bstr <- BS.readFile path
let d = S.runGet getMultiChunks bstr :: Either String ([Float],[Float])
return (Right d)
getMultiChunks :: Get ([Float],[Float])
getMultiChunks = go ([], [])
where
go (l,r) = do
b <- isEmpty
if b then pure ([],[])
else do (lNext, rNext) <- S.get
go (l ++ lNext, r ++ rNext) -- inefficient
So we've written our own getter (untested) that will look to see if byte remain and if so decode another pair of lists of floats. Each time it decodes a new chunk it prepends the old chunk (which is inefficient, use something like a dlist if you want it to be respectable).
My function works ok. But I want to use this function with a file's text. The text file has a word before an integer list. How can I do this?
This is the function:
broke :: Integer -> Integer
broke n = pollard 1 2 n 2 2
The contents of the file is:
Word (11,12)
I want to apply the function broke to the first number.
Well this might be kind of a cheat, but the contents of that file is a valid Haskell expression so you could use Read to do it:
import System.IO (readFile)
data Word = Word (Integer,Integer)
deriving (Read)
main = do
contents <- readFile "path/to/file" -- or wherever your file is
let Word (x,y) = read contents
print $ broke x
The reason this works is that deriving (Read) automatically writes a parser for you, so you get the function read :: String -> Word for free. So this technique is only going to work for files whose contents look like Haskell -- otherwise you will need to write your own parser.
I am trying to write a list into a file and later on I want to read the file contents into the list as well.
So I have a list like this ["ABC","DEF"]
I have tried things like
hPrint fileHandle listName
This just prints into file "["ABC","DEF"]"
I have tried unlines but that is priniting like "ABC\nDEF\n"
Now in both the cases, I cant read back into proper list. The output file has quotes and because of which when I read, I get like this ["["ABC","DEF"]""] i.e a single string in list.
As I am not succeeding in this, I tried to write the list line by line, I tried to apply a map and the function to write the list k = map (\x -> hPrint fileSLC x) fieldsBefore, it is not doing anything, file is blank. I think if I write everything in separate line, I will be able to read like (lines src) later on.
I know whatever I am doing is wrong but I am writing the code on Haskell for second time only, last time I just a wrote a very a small file reading program. Moving from imperative to functional is not that easy. :(
Try using hPutStrLn and unlines instead of hPrint. The hPrint internally calls show which causes Strings to be quoted and escaped.
hPutStr fileHandle (unlines listName)
Alternatively, use a mapM or a forM. A verbose example is:
forM_ listName $ \string ->
hPutStrLn string
This can be simplified ("eta-contracted", in lambda-calculus terminology) to
forM_ listName hPutStrLn
As you have seen, when you read from a file, you get a String. In order to convert this String into a list, you will need to parse it.
For k = map (\x -> hPrint fileSLC x) fieldsBefore to work, you need to use mapM or mapM_ instead of map.
I have question related Haskell language.i need to store bunch of characters in 2D array.How can i store it??I have characters in 10 X 10 format in text file and i want to store it in 2D character array in haskell language.Please help me as soon as possible..thank you..
Here is the code which i tried and in this code i am trying to store value of x in the list named listofchar::
module TreasureFile where
import System.IO
main = do
hdl <- openFile "map.txt" ReadMode
readbychar hdl
readbychar hdl = do
t <- hIsEOF hdl
if t
then return()
else do
let listofchar=[]
x <- hGetChar hdl
if x =='\n'
then putChar '!'--return()
else listofchar x
readbychar hdl
Try this:
import System.IO
main = do
textContents <- readFile "map.txt"
let map = format textContents
print $ map
format text = lines text
Lets step through this program:
First, readFile reads us the file and binds the contents to textContents.
Next we format the contents by splitting the list every time we encounter a newline delimiter and then remove the eventually remaining empty strings.
Done! Now we can do whatever we want with our "map".
A small note on the side:
It will seem strange that our map will be displayed like this:
["aaaaaaaaaa","bbbbbbbbbbb",..] -- doesn't look like 2D map
which is just syntatic sugar for:
[['a','a','a',..],['b','b','b',..],..] -- looks more like a map now
I'm trying to learn Haskell to get used to functional programming languages. I've decided to try a few problems at interviewstreet to start out. I'm having trouble reading from stdin and doing io in general with haskell's lazy io.
Most of the problems have data coming from stdin in the following form:
n
data line 1
data line 2
data line 3
...
data line n
where n is the number of following lines coming from stdin and the next lines are the data.
How do I run my program on each of the n lines one at a time and return the solution to stdout?
I know the stdin input won't be very large but I'm asking about evaluating each line one at a time pretending the input is larger than what can fit in memory just to learn how to use haskell.
You can use interact, in conjunction with lines to process data from stdin one line at a time. Here's an example program that uses interact to access stdin, lines to split the data on each newline, a list comprehension to apply the function perLine to each line of the input, and unlines to put the output from perLine back together again.
main = interact processInput
processInput input = unlines [perLine line | line <- lines input]
perLine line = reverse line -- do whatever you want to 'line' here!
You don't need to worry about the size of the data you're getting over stdin; Haskell's laziness ensures that you only keep the parts you're actually working on in memory at any time.
EDIT: if you still want to work on only the first n lines, you can use the take function in the above example, like this:
processInput input = unlines [perLine line | line <- take 10 (lines input)]
This will terminate the program after the first ten lines have been read and processed.
You can also use a simple recursion:
getMultipleLines :: Int -> IO [String]
getMultipleLines n
| n <= 0 = return []
| otherwise = do
x <- getLine
xs <- getMultipleLines (n-1)
return (x:xs)
And then use it in your main:
main :: IO ()
main = do
line <- getLine
let numLines = read line :: Int
inputs <- getMultipleLines numLines