MissingH-1.4.0.0: src/Data/CSV.hs
{-# LANGUAGE Safe #-}
{- arch-tag: CSV and TSV utilities
Copyright (c) 2005-2011 John Goerzen <jgoerzen@complete.org>
All rights reserved.
For license and copyright information, see the file LICENSE
-}
{- |
Module : Data.CSV
Copyright : Copyright (C) 2005-2011 John Goerzen
License : BSD3
Maintainer : John Goerzen <jgoerzen@complete.org>
Stability : provisional
Portability: portable
Haskell Parsec parsers for comma-separated value (CSV) files.
Written by John Goerzen, jgoerzen\@complete.org
-}
module Data.CSV (csvFile, genCsvFile) where
import Text.ParserCombinators.Parsec
import Data.List (intersperse)
eol :: forall st. GenParser Char st String
eol = (try $ string "\n\r") <|> (try $ string "\r\n") <|> string "\n" <|>
string "\r" <?> "End of line"
cell :: GenParser Char st String
cell = quotedcell <|> many (noneOf ",\n\r")
quotedchar :: GenParser Char st Char
quotedchar = noneOf "\""
<|> (try $ do string "\"\""
return '"'
)
quotedcell :: CharParser st String
quotedcell = do char '"'
content <- many quotedchar
char '"'
return content
line :: GenParser Char st [String]
line = sepBy cell (char ',')
{- | Parse a Comma-Separated Value (CSV) file. The return value is a list of
lines; each line is a list of cells; and each cell is a String.
Please note that CSV files may have a different number of cells on each line.
Also, it is impossible to distinguish a CSV line that has a call with no data
from a CSV line that has no cells.
Here are some examples:
>Input (literal strings) Parses As (Haskell String syntax)
>-------------------------------- ---------------------------------
>1,2,3 [["1", "2", "3"]]
>
>l1 [["l1"], ["l2"]]
>l2
>
> (empty line) [[""]]
>
>NQ,"Quoted" [["NQ", "Quoted"]]
>
>NQ,"Embedded""Quote" [["NQ", "Embedded\"Quote"]]
To parse a String, you might use:
>import Text.ParserCombinators.Parsec
>import Data.String.CSV
>....
>parse csvFile "" mystring
To parse a file, you might instead use:
>do result <- parseFromFile csvFile "/path/to/file"
Please note that the result of parsing will be of type
(Either ParseError [[String]]). A Left result indicates an error.
For more details, see the Parsec information.
-}
csvFile :: CharParser st [[String]]
csvFile = endBy line eol
{- | Generate CSV data for a file. The resulting string can be
written out to disk directly. -}
genCsvFile :: [[String]] -> String
genCsvFile inp =
unlines . map csvline $ inp
where csvline :: [String] -> String
csvline l = concat . intersperse "," . map csvcells $ l
csvcells :: String -> String
csvcells "" = ""
csvcells c = '"' : convcell c ++ "\""
convcell :: String -> String
convcell c = concatMap convchar c
convchar '"' = "\"\""
convchar x = [x]