CSV spreadsheet files are suitable for storing tabular data in a relatively portable way.

The CSV format is flexible but somewhat ill-defined.

For present purposes, authors may assume that the data fields contain no commas, backslashes, or quotation marks.



Task

Read a CSV file, change some values and save the changes back to a file.

For this task we will use the following CSV file:

C1,C2,C3,C4,C5
1,5,9,13,17
2,6,10,14,18
3,7,11,15,19
4,8,12,16,20

Suggestions


 Show how to add a column, headed 'SUM', of the sums of the rows.
 If possible, illustrate the use of built-in or standard functions, methods, or libraries, that handle generic CSV files.



import Data.Array (Array(..), (//), bounds, elems, listArray)
import Data.List (intercalate)
import Control.Monad (when)
import Data.Maybe (isJust)
 
delimiters :: String
delimiters = ",;:"
 
fields :: String -> [String]
fields [] = []
fields xs =
  let (item, rest) = break (`elem` delimiters) xs
      (_, next) = break (`notElem` delimiters) rest
  in item : fields next
 
unfields :: Maybe (Array (Int, Int) String) -> [String]
unfields Nothing = []
unfields (Just a) = every fieldNumber $ elems a
  where
    ((_, _), (_, fieldNumber)) = bounds a
    every _ [] = []
    every n xs =
      let (y, z) = splitAt n xs
      in intercalate "," y : every n z
 
fieldArray :: [[String]] -> Maybe (Array (Int, Int) String)
fieldArray [] = Nothing
fieldArray xs =
  Just $ listArray ((1, 1), (length xs, length $ head xs)) $ concat xs
 
fieldsFromFile :: FilePath -> IO (Maybe (Array (Int, Int) String))
fieldsFromFile = fmap (fieldArray . map fields . lines) . readFile
 
fieldsToFile :: FilePath -> Maybe (Array (Int, Int) String) -> IO ()
fieldsToFile f = writeFile f . unlines . unfields
 
someChanges :: Maybe (Array (Int, Int) String)
            -> Maybe (Array (Int, Int) String)
someChanges =
  fmap (// [((1, 1), "changed"), ((3, 4), "altered"), ((5, 2), "modified")])
 
main :: IO ()
main = do
  a <- fieldsFromFile "example.txt"
  when (isJust a) $ fieldsToFile "output.txt" $ someChanges a