csv-enumerator 0.9.0 → 0.9.2
raw patch · 4 files changed
+95/−37 lines, 4 filesdep ~attoparsecdep ~attoparsec-enumeratordep ~enumeratorPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: attoparsec, attoparsec-enumerator, enumerator
API changes (from Hackage documentation)
+ Data.CSV.Enumerator: mapAccumCSVFile :: CSVeable r => FilePath -> CSVSettings -> (acc -> r -> (acc, [r])) -> acc -> FilePath -> IO (Either SomeException acc)
- Data.CSV.Enumerator: CSVS :: Char -> Maybe Char -> Maybe Char -> Char -> CSVSettings
+ Data.CSV.Enumerator: CSVS :: !Char -> !Maybe Char -> !Maybe Char -> !Char -> CSVSettings
- Data.CSV.Enumerator: csvOutputColSep :: CSVSettings -> Char
+ Data.CSV.Enumerator: csvOutputColSep :: CSVSettings -> !Char
- Data.CSV.Enumerator: csvOutputQuoteChar :: CSVSettings -> Maybe Char
+ Data.CSV.Enumerator: csvOutputQuoteChar :: CSVSettings -> !Maybe Char
- Data.CSV.Enumerator: csvQuoteChar :: CSVSettings -> Maybe Char
+ Data.CSV.Enumerator: csvQuoteChar :: CSVSettings -> !Maybe Char
- Data.CSV.Enumerator: csvSep :: CSVSettings -> Char
+ Data.CSV.Enumerator: csvSep :: CSVSettings -> !Char
- Data.CSV.Enumerator: writeHeaders :: CSVeable r => CSVSettings -> Handle -> [r] -> IO ()
+ Data.CSV.Enumerator: writeHeaders :: CSVeable r => CSVSettings -> Handle -> [r] -> IO Bool
Files
- csv-enumerator.cabal +5/−4
- src/Data/CSV/Enumerator.hs +57/−22
- src/Data/CSV/Enumerator/Types.hs +4/−4
- test/csv-enumerator-test.cabal +29/−7
csv-enumerator.cabal view
@@ -1,5 +1,5 @@ Name: csv-enumerator-Version: 0.9.0+Version: 0.9.2 Synopsis: A flexible, fast, enumerator-based CSV parser library for Haskell. Homepage: http://github.com/ozataman/csv-enumerator License: BSD3@@ -60,15 +60,16 @@ Other-modules: Data.CSV.Enumerator.Types build-depends:- attoparsec >= 0.8 && < 0.10- , attoparsec-enumerator >= 0.2.0.3+ attoparsec >= 0.8+ , attoparsec-enumerator >= 0.2 , base >= 4 && < 5 , containers >= 0.3 , directory , bytestring- , enumerator >= 0.4.5 && < 0.5+ , enumerator >= 0.4.5 , transformers >= 0.2 , safe , unix-compat >= 0.2.1.1 extensions: ScopedTypeVariables+ ghc-options: -funbox-strict-fields
src/Data/CSV/Enumerator.hs view
@@ -32,6 +32,7 @@ -- * Mapping Over CSV Files , mapCSVFile+ , mapAccumCSVFile , mapIntoHandle -- * Primitive Iteratees@@ -307,6 +308,30 @@ ------------------------------------------------------------------------------+-- | Map-accumulate over a CSV file. Similar to 'mapAccumL' in 'Data.List'.+mapAccumCSVFile+ :: (CSVeable r)+ => FilePath+ -> CSVSettings+ -> (acc -> r -> (acc, [r]))+ -> acc+ -> FilePath+ -> IO (Either SomeException acc)+mapAccumCSVFile fi s f acc fo = do+ res <- foldCSVFile fi s iter (acc, (Nothing, 0))+ return $ fst `fmap` res+ where+ iter (a, outa) (ParsedRow (Just !r)) = foldM chain (a', outa) rs+ where (a', rs) = f a r+ iter (a, outa) x = do+ outa' <- fileSink s fo outa x+ return $ (a, outa')+ chain (a, outa) !r = do+ outa' <- fileSink s fo outa (ParsedRow (Just r))+ return $ (a, outa')+++------------------------------------------------------------------------------ readCSVFile :: (CSVeable r) => CSVSettings -- ^ CSV settings -> FilePath -- ^ FilePath -> IO (Either SomeException [r]) -- ^ Collected data@@ -337,19 +362,19 @@ -> [r] -- ^ Data to be output -> IO Int -- ^ Number of rows written appendCSVFile s fp rs = - let doOutput (c,h) = when c (writeHeaders s h rs) >> outputRowsIter h+ let doOutput (c,h) = when c (writeHeaders s h rs >> return ()) >> outputRowsIter h outputRowsIter h = foldM (step h) 0 . map (rowToStr s) $ rs step h acc x = (B.hPutStrLn h x) >> return (acc+1) chkOpen = do- writeHeaders <- do+ wrHeader <- do fe <- doesFileExist fp if fe- then do- fs <- getFileStatus fp >>= return . fileSize- return $ if fs > 0 then False else True+ then do+ fs <- getFileStatus fp >>= return . fileSize+ return $ if fs > 0 then False else True else return True h <- openFile fp AppendMode- return (writeHeaders, h)+ return (wrHeader, h) in bracket (chkOpen) (hClose . snd)@@ -380,10 +405,10 @@ -writeHeaders :: CSVeable r => CSVSettings -> Handle -> [r] -> IO ()+writeHeaders :: CSVeable r => CSVSettings -> Handle -> [r] -> IO Bool writeHeaders s h rs = case fileHeaders rs of- Just hs -> B.hPutStrLn h . rowToStr s $ hs- Nothing -> return ()+ Just hs -> (B.hPutStrLn h . rowToStr s) hs >> return True+ Nothing -> return False outputRowIter :: CSVeable r => CSVSettings -> Handle -> r -> E.Iteratee B.ByteString IO ()@@ -468,10 +493,12 @@ where f' acc EOF = return acc f' acc (ParsedRow Nothing) = return acc- f' (False, _) r'@(ParsedRow (Just r)) = do+ f' (False, i) r'@(ParsedRow (Just r)) = do rs <- f r- when outh $ writeHeaders csvs h rs- f' (True, 0) r'+ headerDone <- if outh then writeHeaders csvs h rs else return True+ if headerDone + then f' (headerDone, 0) r' -- Headers are done, now process row+ else return (False, i+1) -- Problem in this row, move on to next f' (True, !i) (ParsedRow (Just r)) = do rs <- f r outputRows csvs h rs@@ -487,9 +514,13 @@ in a' `seq` yield a' (E.Chunks []) collectRows acc (ParsedRow Nothing) = yield acc (E.Chunks []) --- * Parsers -rowParser :: (Monad m, MonadIO m) => CSVSettings -> E.Iteratee B.ByteString m (Maybe Row)+------------------------------------------------------------------------------+-- Parsers++rowParser + :: (Monad m, MonadIO m) + => CSVSettings -> E.Iteratee B.ByteString m (Maybe Row) rowParser csvs = E.catchError p handler where p = iterParser $ row csvs@@ -497,6 +528,7 @@ liftIO $ putStrLn ("Error in parsing: " ++ show e) yield Nothing (E.Chunks []) + row :: CSVSettings -> Parser (Maybe Row) row csvs = csvrow csvs <|> badrow @@ -506,8 +538,8 @@ csvrow :: CSVSettings -> Parser (Maybe Row) csvrow c = - let !rowbody = (quotedField' <|> (field c)) `sepBy` C8.char (csvSep c)- !properrow = rowbody <* (C8.endOfLine <|> P.endOfInput)+ let rowbody = (quotedField' <|> (field c)) `sepBy` C8.char (csvSep c)+ properrow = rowbody <* (C8.endOfLine <|> P.endOfInput) quotedField' = case csvQuoteChar c of Nothing -> mzero Just q' -> try (quotedField q')@@ -516,7 +548,7 @@ return $ Just res field :: CSVSettings -> Parser Field-field s = P.takeWhile (isFieldChar s) <?> "Parsing a regular field"+field s = P.takeWhile (isFieldChar s) isFieldChar s = notInClass xs' where xs = csvSep s : "\n\r"@@ -525,10 +557,13 @@ Just x -> x : xs quotedField :: Char -> Parser Field-quotedField c = let w = c2w c in do- (C8.char c) <?> "Quote start"- f <- many (notWord8 w <|> (string (B.pack $ [w,w]) *> return w))- (C8.char c) <?> "Quote end"- return $ B.pack f+quotedField c = + let quoted = string dbl *> return c+ dbl = B8.pack [c,c]+ in do+ C8.char c + f <- many (C8.notChar c <|> quoted)+ C8.char c + return $ B8.pack f
src/Data/CSV/Enumerator/Types.hs view
@@ -13,16 +13,16 @@ data CSVSettings = CSVS { -- | Separator character to be used in between fields- csvSep :: Char + csvSep :: !Char -- | Quote character that may sometimes be present around fields. If 'Nothing' is given, the library will never expect quotation even if it is present.- , csvQuoteChar :: Maybe Char+ , csvQuoteChar :: !(Maybe Char) -- | Quote character that should be used in the output.- , csvOutputQuoteChar :: Maybe Char+ , csvOutputQuoteChar :: !(Maybe Char) -- | Field separator that should be used in the output.- , csvOutputColSep :: Char+ , csvOutputColSep :: !Char } deriving (Read, Show, Eq) -- | Default settings for a CSV file.
test/csv-enumerator-test.cabal view
@@ -9,14 +9,7 @@ Category: Data Build-type: Simple Cabal-version: >=1.2-Description:- For more information and examples, check out the README at:- <http://github.com/ozataman/csv-enumerator>.- - The API is fairly well documented and I would encourage you to keep your- haddocks handy. - extra-source-files: README.markdown @@ -45,3 +38,32 @@ extensions: ScopedTypeVariables OverloadedStrings+++Executable bench+ main-is: Bench.hs+ hs-source-dirs: ./ ../src+ Other-modules:+ Data.CSV.Enumerator.Types+ Data.CSV.Enumerator+ build-depends:+ attoparsec >= 0.8 && < 0.10+ , attoparsec-enumerator >= 0.2.0.3+ , base >= 4 && < 5+ , containers >= 0.3+ , directory+ , bytestring+ , enumerator >= 0.4.5 && < 0.5+ , transformers >= 0.2+ , safe+ , unix-compat >= 0.2.1.1+ , test-framework+ , test-framework-quickcheck2+ , test-framework-hunit+ , QuickCheck >= 2+ , HUnit >= 1.2+ extensions:+ ScopedTypeVariables+ OverloadedStrings+ ghc-options: -rtsopts + ghc-prof-options: -rtsopts -caf-all -auto-all