我使用attoparsec
编写了以下解析代码:
data Test = Test {
a :: Int,
b :: Int
} deriving (Show)
testParser :: Parser Test
testParser = do
a <- decimal
tab
b <- decimal
return $ Test a b
tParser :: Parser [Test]
tParser = many' $ testParser <* endOfLine
这适用于小型文件,我执行它是这样的:
main :: IO ()
main = do
text <- TL.readFile "./testFile"
let (Right a) = parseOnly (manyTill anyChar endOfLine *> tParser) text
print a
但是当文件大小超过70MB时,它会占用大量内存。作为解决方案,我想我会使用attoparsec-conduit
。经过他们API后,我不确定如何让他们一起工作。我的解析器的类型为Parser Test
,但sinkParser
实际上接受Parser a b
类型的解析器。我对如何在常量内存中执行此解析器感兴趣? (基于管道的解决方案也是可以接受的,但我不习惯Pipes API。)
答案 0 :(得分:5)
Parser
的第一个类型参数只是输入的数据类型(Text
或ByteString
)。您可以将testParser
函数作为sinkParser
的参数提供,它可以正常工作。这是一个简短的例子:
{-# LANGUAGE OverloadedStrings #-}
import Conduit (liftIO, mapM_C, runResourceT,
sourceFile, ($$), (=$))
import Data.Attoparsec.Text (Parser, decimal, endOfLine, space)
import Data.Conduit.Attoparsec (conduitParser)
data Test = Test {
a :: Int,
b :: Int
} deriving (Show)
testParser :: Parser Test
testParser = do
a <- decimal
space
b <- decimal
endOfLine
return $ Test a b
main :: IO ()
main = runResourceT
$ sourceFile "foo.txt"
$$ conduitParser testParser
=$ mapM_C (liftIO . print)
答案 1 :(得分:5)
以下是pipes
解决方案(假设您使用的是基于Text
的解析器):
import Pipes
import Pipes.Text.IO (fromHandle)
import Pipes.Attoparsec (parsed)
import qualified System.IO as IO
main = IO.withFile "./testfile" IO.ReadMode $ \handle -> runEffect $
for (parsed (testParser <* endOfLine) (fromHandle handle)) (lift . print)