我有一个文件,它看起来像这样index : label
,指数的价值包含在0... 100000000
和标签的范围键分割110Mo文件可以是任何String
价值,我想分裂这个文件,该文件每片100片中有110片每片有一片计算。我怎样才能做到这一点?如何哈斯克尔
123 : "acgbdv"
127 : "ytehdh"
129 : "yhdhgdt"
...
9898657 : "bdggdggd"
我有一个文件,它看起来像这样index : label
,指数的价值包含在0... 100000000
和标签的范围键分割110Mo文件可以是任何String
价值,我想分裂这个文件,该文件每片100片中有110片每片有一片计算。我怎样才能做到这一点?如何哈斯克尔
123 : "acgbdv"
127 : "ytehdh"
129 : "yhdhgdt"
...
9898657 : "bdggdggd"
如果您使用字符串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线。
类似字符串和文本的包通常提供像lines
和hGetContents
这样的函数,所以您应该可以轻松地将此函数适用于它们中的任何一个。
重要的是要知道你正在处理每个片的结果,因为你不想让这些数据超过必要的时间。理想情况下,在计算完每个切片后,数据将被完全消耗并且可能被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
正在做什么类型的工作,但它会涉及enumLines
和take
。
是否有必要一次处理完整的100行,还是只是为了提高效率而分段处理?如果是后者,不要担心。使用实际的折叠函数或类似于processFileMapping的函数,一次最好处理一行。
什么是“Mo”? – jrockway 2010-09-13 18:43:01
万对象? – kennytm 2010-09-13 18:48:10
这是法国人的“MB” - 巨型八位字节。 – Chuck 2010-09-13 23:19:41