我为一个大的csv文件写了一个解析器,它在一个较小的子集上工作,但在大约150万行(实际文件)的内存中耗尽。在最初将所有元素解析成一个列表(使用manyTill)之后,我转而使用解析器状态将它们存储在单个二进制搜索树中-这适用于大文件。
从那以后,我将“元素类型”分成了三个不同的类型,并希望将它们存储在自己的树中,从而产生三个不同类型的树。然而,这个版本只适用于较小的测试文件,而对于较大的测试文件则会耗尽内存。
import qualified Data.Tree.AVL as AVL
import qualified Text.ParserCombinators.Parsec as Parsec
----
data ENW = ENW (AVL.AVL Extent) (AVL.AVL Node) (AVL.AVL Way)
---- used to be Element = Extent | Node | Way in a (Tree Element) - this worked
csvParser :: Parsec String ENW ENW
csvParser = do (Parsec.manyTill (parseL) Parsec.eof) >> Parsec.getState
where parseL = parseLine >> ((Parsec.newline >> return ()) <|> Parsec.eof)
parseLine :: Parsec String ENW ()
parseLine = parseNode <|> parseWay <|> parseExtents
parseNode :: Parsec String ENW ()
parseNode = Parsec.string "node" *> (flip addNode <$> (Node <$> identifier <*> float <*> float)) >>= Parsec.updateState
where identifier = Parsec.tab *> (read <$> Parsec.many1 Parsec.digit)
float = Parsec.tab *> (read <$> parseFloat)
addNode :: ENW -> Node -> ENW
addNode (ENW e n w) node = (ENW e (AVL.push (sndCC node) node n) w)parseWay和parseExtent遵循相同的模式,整个过程从
Parsec.runParser csvParser (ENW AVL.empty AVL.empty AVL.empty) "" input我不明白为什么使用三个较小的树而不是一个大的树会导致内存问题。
发布于 2015-06-14 12:10:52
你有充分的理由不使用Cassava吗?它可用于流式传输CSV数据,并且可能比即席CSV解析器更健壮。我自己使用它的经验表明,它具有出色的性能,并且可以很容易地扩展到解析您自己的类型。
编辑:看起来你使用的是制表符分隔的值数据,而不是逗号分隔的数据,但是Cassava允许你指定要分割列的分隔符,它还显示你在每行上的数据可能不同,所以你可能需要使用Cassava的“原始”格式,它为每一行返回一个矢量ByteString,然后你可以根据第一个元素进行解析。
我从来没有见过有人使用AVL tree包,你有没有一个好的理由不使用更多的标准结构?这个包很旧(上一次更新是在2008年),最近的包可能会表现得更好。
https://stackoverflow.com/questions/30825686
复制相似问题