From 8827bc2753074a38042ebd9b7ac75401206b070d Mon Sep 17 00:00:00 2001 From: Johan Tibell Date: Tue, 28 Aug 2012 17:39:04 -0700 Subject: [PATCH] Add an option to skip the header in decodeWith --- Data/Csv/Encoding.hs | 2 ++ Data/Csv/Parser.hs | 21 +++++++++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Data/Csv/Encoding.hs b/Data/Csv/Encoding.hs index 2d15921..c9a73d7 100644 --- a/Data/Csv/Encoding.hs +++ b/Data/Csv/Encoding.hs @@ -56,6 +56,8 @@ import Data.Csv.Types -- | Efficiently deserialize CSV records from a lazy 'L.ByteString'. -- If this fails due to incomplete or invalid input, @'Left' msg@ is -- returned. Equivalent to @'decodeWith' 'defaultDecodeOptions'@. +-- +-- Use the 'decSkipHeader' and 'decodeWith' if the input has a header. decode :: FromRecord a => L.ByteString -> Either String (Vector a) decode = decodeWith defaultDecodeOptions {-# INLINE decode #-} diff --git a/Data/Csv/Parser.hs b/Data/Csv/Parser.hs index aaba190..103bc4a 100644 --- a/Data/Csv/Parser.hs +++ b/Data/Csv/Parser.hs @@ -27,6 +27,7 @@ module Data.Csv.Parser import Blaze.ByteString.Builder (fromByteString, toByteString) import Blaze.ByteString.Builder.Char.Utf8 (fromChar) import Control.Applicative +import Control.Monad (when) import Data.Attoparsec.Char8 hiding (Parser, Result, parse) import qualified Data.Attoparsec as A import qualified Data.Attoparsec.Lazy as AL @@ -46,17 +47,25 @@ import Data.Csv.Types data DecodeOptions = DecodeOptions { -- | Field delimiter. decDelimiter :: {-# UNPACK #-} !Word8 + + -- | If 'True', the CSV file must have a header and this header + -- will be skipped. + , decSkipHeader :: !Bool } -- | Decoding options for parsing CSV files. defaultDecodeOptions :: DecodeOptions defaultDecodeOptions = DecodeOptions - { decDelimiter = 44 -- comma + { decDelimiter = 44 -- comma + , decSkipHeader = False } -- | Parse a CSV file that does not include a header. csv :: DecodeOptions -> AL.Parser Csv csv !opts = do + when (decSkipHeader opts) $ do + _ <- header (decDelimiter opts) + return () vals <- record (decDelimiter opts) `sepBy1` endOfLine _ <- optional endOfLine endOfInput @@ -66,7 +75,11 @@ csv !opts = do -- | Parse a CSV file that includes a header. csvWithHeader :: DecodeOptions -> AL.Parser (Header, V.Vector NamedRecord) -csvWithHeader !opts = do +csvWithHeader !opts + | decSkipHeader opts = moduleError "csvWithHeader" $ + "decSkipHeader must not be used together with " ++ + "header-based parsing" +csvWithHeader opts = do hdr <- header (decDelimiter opts) vals <- map (toNamedRecord hdr) . removeBlankLines <$> (record (decDelimiter opts)) `sepBy1` endOfLine @@ -155,3 +168,7 @@ doubleQuote, newline, cr :: Word8 doubleQuote = 34 newline = 10 cr = 13 + +moduleError :: String -> String -> a +moduleError func msg = error $ "Data.Csv.Parser." ++ func ++ ": " ++ msg +{-# NOINLINE moduleError #-}