2010-09-13 98 views
2

我有一个文件,它看起来像这样index : label指数的价值包含在0... 100000000标签的范围键分割110Mo文件可以是任何String价值,我想分裂这个文件,该文件每片100片中有110片每片有一片计算。我怎样才能做到这一点?如何哈斯克尔

123 : "acgbdv" 

127 : "ytehdh" 

129 : "yhdhgdt" 

... 

9898657 : "bdggdggd" 
+3

什么是“Mo”? – jrockway 2010-09-13 18:43:01

+0

万对象? – kennytm 2010-09-13 18:48:10

+2

这是法国人的“MB” - 巨型八位字节。 – Chuck 2010-09-13 23:19:41

回答

3

如果您使用字符串IO,你可以做到以下几点:

import System.IO 
import Control.Monad 

-- | Process 100 lines 
process100 :: [String] -> MyData 
-- whatever this function does 

loop :: [String] -> [MyData] 
loop lns = go [] lns 
    where 
    go acc [] = reverse acc 
    go acc lns = let (this, next) = splitAt 100 lns in go (process100 this:acc) next 

processFile :: FilePath -> IO [MyData] 
processFile f = withFile f ReadMode (fmap (loop . lines) . hGetContents) 

注意,这个函数会默默地处理最后一块,即使它不完全是100线。

类似字符串和文本的包通常提供像lineshGetContents这样的函数,所以您应该可以轻松地将此函数适用于它们中的任何一个。

重要的是要知道你正在处理每个片的结果,因为你不想让这些数据超过必要的时间。理想情况下,在计算完每个切片后,数据将被完全消耗并且可能被gc'd。通常要么单独的结果被合并成一个单一的数据结构(一个“折叠”),要么每个单独处理(可能会输出一行到一个文件或类似的东西)。如果它是一个折,你应该改变“循环”,看起来像这样:

loopFold :: [String] -> MyData -- assuming there is a Monoid instance for MyData 
loopFold lns = go mzero lns 
    where 
    go !acc [] = acc 
    go !acc lns = let (this, next) = splitAt 100 lns in go (process100 this `mappend` acc) next 

loopFold功能使用爆炸方式迫使“迈德特”的评价(使用“语言BangPatterns”编译启用)。根据MyData的不同,您可能需要使用deepseq以确保其完全评估。

相反,如果你在写每一行输出,离开loop,因为它是和更改processFile

processFileMapping :: FilePath -> IO() 
processFileMapping f = withFile f ReadMode pf 
    where 
    pf = mapM_ (putStrLn . show) <=< fmap (loop . lines) . hGetContents 

如果你有兴趣枚举/ iteratee风格的处理,这是一个非常简单的问题。我不能不知道process100正在做什么类型的工作,但它会涉及enumLinestake

是否有必要一次处理完整的100行,还是只是为了提高效率而分段处理?如果是后者,不要担心。使用实际的折叠函数或类似于processFileMapping的函数,一次最好处理一行。