我有一个279MB的文件,其中包含大约1000万个键/值对,大约有500,000个唯一键。它按键分组(每个键只需要写一次),因此给定键的所有值都在一起。
我想要做的是转换关联,创建一个文件,其中对按值分组,并且给定值的所有键都存储在一起。
目前,我正在使用Parsec作为元组列表(K,[V])
读取(使用惰性IO,因此我可以将其作为流处理,而Parsec正在处理输入文件),其中:
newtype K = K Text deriving (Show, Eq, Ord, Hashable)
newtype V = V Text deriving (Show, Eq, Ord, Hashable)
tupleParser :: Parser (K,[V])
tupleParser = ...
data ErrList e a = Cons a (ErrList e a) | End | Err e
parseAllFromFile :: Parser a -> FilePath-> IO (ErrList ParseError a)
parseAllFromFile parser inputFile = do
contents <- readFile inputFile
let Right initialState = parse getParserState inputFile contents
return $ loop initialState
where loop state = case unconsume $ runParsecT parser' state of
Error err -> Err err
Ok Nothing _ _ -> End
Ok (Just a) state' _ -> a `Cons` loop state'
unconsume v = runIdentity $ case runIdentity v of
Consumed ma -> ma
Empty ma -> ma
parser' = (Just <$> parser) <|> (const Nothing <$> eof)
我尝试将元组插入Data.HashMap.Map V [K]
以转置关联:
transpose :: ErrList ParseError (K,[V]) -> Either ParseError [(V,[K])]
transpose = transpose' M.empty
where transpose' _ (Err e) = Left e
transpose' m End = Right $ assocs m
transpose' m (Cons (k,vs) xs) = transpose' (L.foldl' (include k) m vs) xs
include k m v = M.insertWith (const (k:)) v [k] m
但是当我尝试它时,我收到了错误:
memory allocation failed (requested 2097152 bytes)
我能想到一些我做错的事情:
我现在倾向于(1),但我无论如何都不确定。
答案 0 :(得分:1)
数据是否有可能增加?如果是,那么我建议不要将while文件读入内存并以另一种方式处理数据。
一个简单的可能性就是使用关系数据库。这非常简单 - 只需加载数据,创建适当的索引并根据需要对其进行排序。数据库将为您完成所有工作。我肯定会推荐这个。
另一种选择是创建自己的基于文件的机制。例如:
l
。n = d `div` l
个文件,其中d
是您的数据总量。 (希望这不会超过您的文件描述符限制。您也可以在每次操作后关闭并重新打开文件,但这会使该过程非常慢。)(k,v)
放入文件编号hash v `mod` l
。这可确保具有相同值v
的对将最终位于同一文件中。它本质上是一个带文件桶的哈希表。此解决方案假设每个值具有大致相同数量的键(否则某些文件可能会异常大)。
您还可以实现external sort,这样您就可以对任何数量的数据进行排序。
答案 1 :(得分:-1)
要允许大于可用内存的文件,最好一次以一口大小的方式处理它们。
这是将文件A复制到新文件B的实体算法:
将文件A复制到临时文件夹并在使用它时锁定它也是值得的,这样就不会阻止网络上的其他人更改原始文件,但是你有文件的快照正如程序开始时那样。
我打算将来重新回答这个问题并添加代码。