{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
module DataFrame.IO.CSV (
readCsv,
readTsv,
readCsvWithOpts,
readSeparated,
readCsvWithSchema,
CsvReader,
schemaReadOptions,
decodeSeparated,
fromCsv,
fromCsvBytes,
module DataFrame.IO.CSV.Internal.Options,
writeCsv,
writeTsv,
writeSeparated,
stripQuotes,
) where
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import qualified Data.Map.Strict as M
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Data.Text.IO as TIO
import Control.Exception (SomeException, catch)
import Data.Maybe (fromMaybe)
import DataFrame.IO.CSV.Internal.Options
import DataFrame.IO.CSV.Internal.Read (decodeCsvStrict)
import DataFrame.Internal.DataFrame (DataFrame (..), toSeparated)
import DataFrame.Schema (Schema, elements)
readCsv :: FilePath -> IO DataFrame
readCsv :: FilePath -> IO DataFrame
readCsv = ReadOptions -> FilePath -> IO DataFrame
readSeparated ReadOptions
defaultReadOptions
type CsvReader = ReadOptions -> FilePath -> IO DataFrame
schemaReadOptions :: Schema -> ReadOptions
schemaReadOptions :: Schema -> ReadOptions
schemaReadOptions Schema
schema =
ReadOptions
defaultReadOptions
{ typeSpec =
SpecifyTypes (M.toList (elements schema)) (typeSpec defaultReadOptions)
, readColumns = Just (M.keys (elements schema))
}
readCsvWithSchema :: Schema -> FilePath -> IO DataFrame
readCsvWithSchema :: Schema -> FilePath -> IO DataFrame
readCsvWithSchema Schema
schema =
ReadOptions -> FilePath -> IO DataFrame
readSeparated
ReadOptions
defaultReadOptions
{ typeSpec =
SpecifyTypes
(M.toList (elements schema))
(typeSpec defaultReadOptions)
}
readCsvWithOpts :: ReadOptions -> FilePath -> IO DataFrame
readCsvWithOpts :: ReadOptions -> FilePath -> IO DataFrame
readCsvWithOpts = ReadOptions -> FilePath -> IO DataFrame
readSeparated
readTsv :: FilePath -> IO DataFrame
readTsv :: FilePath -> IO DataFrame
readTsv = ReadOptions -> FilePath -> IO DataFrame
readSeparated (ReadOptions
defaultReadOptions{columnSeparator = '\t'})
readSeparated :: ReadOptions -> FilePath -> IO DataFrame
readSeparated :: ReadOptions -> FilePath -> IO DataFrame
readSeparated ReadOptions
opts FilePath
path = do
ReadOptions -> IO ()
validateReadOptions ReadOptions
opts
let stripUtf8Bom :: ByteString -> ByteString
stripUtf8Bom ByteString
b = ByteString -> Maybe ByteString -> ByteString
forall a. a -> Maybe a -> a
fromMaybe ByteString
b (ByteString -> ByteString -> Maybe ByteString
BS.stripPrefix ByteString
"\xEF\xBB\xBF" ByteString
b)
ByteString
csvData <- ByteString -> ByteString
stripUtf8Bom (ByteString -> ByteString) -> IO ByteString -> IO ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> FilePath -> IO ByteString
BS.readFile FilePath
path
ReadOptions -> ByteString -> IO DataFrame
decodeCsvStrict ReadOptions
opts ByteString
csvData
decodeSeparated :: ReadOptions -> BL.ByteString -> IO DataFrame
decodeSeparated :: ReadOptions -> ByteString -> IO DataFrame
decodeSeparated ReadOptions
opts ByteString
csvData = ReadOptions -> ByteString -> IO DataFrame
decodeCsvStrict ReadOptions
opts (ByteString -> ByteString
BL.toStrict ByteString
csvData)
writeCsv :: FilePath -> DataFrame -> IO ()
writeCsv :: FilePath -> DataFrame -> IO ()
writeCsv = Char -> FilePath -> DataFrame -> IO ()
writeSeparated Char
','
writeTsv :: FilePath -> DataFrame -> IO ()
writeTsv :: FilePath -> DataFrame -> IO ()
writeTsv = Char -> FilePath -> DataFrame -> IO ()
writeSeparated Char
'\t'
writeSeparated ::
Char ->
FilePath ->
DataFrame ->
IO ()
writeSeparated :: Char -> FilePath -> DataFrame -> IO ()
writeSeparated Char
c FilePath
filepath DataFrame
df = FilePath -> Text -> IO ()
TIO.writeFile FilePath
filepath (Char -> DataFrame -> Text
toSeparated Char
c DataFrame
df)
fromCsv :: String -> IO (Either String DataFrame)
fromCsv :: FilePath -> IO (Either FilePath DataFrame)
fromCsv FilePath
s = do
let bs :: ByteString
bs = ByteString -> ByteString
BL.fromStrict (Text -> ByteString
TE.encodeUtf8 (FilePath -> Text
T.pack FilePath
s))
(DataFrame -> Either FilePath DataFrame
forall a b. b -> Either a b
Right (DataFrame -> Either FilePath DataFrame)
-> IO DataFrame -> IO (Either FilePath DataFrame)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadOptions -> ByteString -> IO DataFrame
decodeSeparated ReadOptions
defaultReadOptions ByteString
bs)
IO (Either FilePath DataFrame)
-> (SomeException -> IO (Either FilePath DataFrame))
-> IO (Either FilePath DataFrame)
forall e a. Exception e => IO a -> (e -> IO a) -> IO a
`catch` (\(SomeException
e :: SomeException) -> Either FilePath DataFrame -> IO (Either FilePath DataFrame)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (FilePath -> Either FilePath DataFrame
forall a b. a -> Either a b
Left (SomeException -> FilePath
forall a. Show a => a -> FilePath
show SomeException
e)))
fromCsvBytes :: BL.ByteString -> IO DataFrame
fromCsvBytes :: ByteString -> IO DataFrame
fromCsvBytes = ReadOptions -> ByteString -> IO DataFrame
decodeSeparated ReadOptions
defaultReadOptions
stripQuotes :: T.Text -> T.Text
stripQuotes :: Text -> Text
stripQuotes Text
txt =
case Text -> Maybe (Char, Text)
T.uncons Text
txt of
Just (Char
'"', Text
rest) ->
case Text -> Maybe (Text, Char)
T.unsnoc Text
rest of
Just (Text
middle, Char
'"') -> Text
middle
Maybe (Text, Char)
_ -> Text
txt
Maybe (Char, Text)
_ -> Text
txt