packages feed

Emping-0.5: src/CSVParse.hs

{- | Emping 0.4, 0.5 (provisional)

Module CSVParse converts a table in Open Office Calc .csv format into a list of lists of Haskell Strings.The first row in the table must consist of the attribute names. It is recommended that all attributes have unique names, but blanks and doubles are allowed. Each attribute will be identified by its column number.

The rows after the first must be attribute values. Values in different columns may have the same names or be blank. Values in the same column with the same names are considered to be equal. Emping will generate a list of values for each column.

Warning: if you use blank fields in the table, be sure they are correctly seperated by comma's and that each row is correctly terminated by a newline.

Fri 04 Apr 2008 07:17:12 PM CEST 
-}
module CSVParse ( getTable , blankId  ) where

import Text.ParserCombinators.Parsec

{- |
a text field starts with \" and ends with \"

a numerical field consists of digits only 

an empty field is zero or more spaces separated by comma's, returns \"NA\"
-}

comma:: GenParser Char a Char
comma = char ','

-- | textField returns any String between double quotes (the OO Calc format)
txtField :: GenParser Char a String
txtField = do  char '\"'  
               txt <- many (noneOf "\"") 
               char '\"'; return txt 

-- | numField returns a string of digits as a string. This is the OO Calc format for numbers.
numField :: GenParser Char a String              
numField = many1 digit

-- | blankId is the String returned from parsing a blank field. Currently this is: \"NA\", which stands for 'not applicable'.
blankId :: String
blankId = "NA"

-- | noField parses a blank field
noField :: GenParser Char a String
noField = do { many (char ' '); return blankId }

-- | getField parses a text field, a numeric field, or a blank field
getField :: GenParser Char a String
getField = do {txtField <|> numField <|> noField }

-- | csvLine parses a row
csvLine :: GenParser Char a [String]
csvLine = do  fields <- (sepBy getField comma)
              newline 
              return fields

-- | csvTable parses the table
csvTable :: GenParser Char a [[String]]
csvTable = many csvLine

-- | getTable reads the table from a file path and returns a list of row representations, each itself a list of strings
getTable :: String -> IO [[String]]
getTable fname = do pares <- parseFromFile csvTable fname
                    let res = case pares of 
                               Right x -> x
                               Left err -> error (show err)
                    return (res)  

{-
-- test the table in csv format for parse errors

test = do putStrLn "Enter CSV file:"
          name <- getLine
          res <- parseFromFile csvTable name
          case res of Left err -> print err
                      Right xs -> print xs
-}