2013-02-24 51 views
14

在Haskell中处理真正大的二进制文件的最有效方法是什么?在大文件中寻找最有效的方法

标准答案是读取整个文件作为一个懒惰的字节字符串,然后使用类似的二进制包到超过它写一个解析器。有几个问题...

首先,库像二进制不真正处理解析失败,我明确期待解析有时失败。

其次,我不解析整个文件的内容。我会跳过它的大块。并且从磁盘读取千兆字节的数据到RAM中,让垃圾收集器再次丢弃它似乎相当不正常。

相关的是,我需要能够告诉我,如果要执行跳过会带我过的文件或不结束(和错误,如果它做)。

我也可能需要寻求向后,或者可能到文件中的特定字节偏移量,这似乎不被懒惰的ByteString方法很好地支持。 (存在将整个文件保存在RAM中的严重危险。)

当然,替代方法是逐个读取单个字节,并与hSeek命令交错。但现在问题是,一次读取一个字节的文件的效率如何?这听起来像它可能是相当缓慢。我不确定hSetBuffering对此有影响。 (?)

然后,当然还有mmap。但是,如果在大文件上使用虚拟内存系统,这似乎会吓倒虚拟内存系统。 (这很奇怪,考虑到它存在的全部目的......)

我们怎么看,乡亲们?就I/O性能和代码可维护性而言,处理此问题的最佳方法是什么?

+2

什么'hGetBuf','hGetBufNonBlocking'和'hGetSome'?您可以读取比单个字节更大的thunk。 – 2013-02-24 15:04:47

+2

使用[一mmap'd字节串(http://hackage.haskell.org/packages/archive/bytestring-mmap/0.2.2/doc/html/System-IO-Posix-MMap-Lazy.html)?索引然后只从内存加载所需的块。 – 2013-02-24 15:47:07

+0

我徘徊如何所有这些与'hSetBuffering'交互? – MathematicalOrchid 2013-03-07 19:34:09

回答

2

我在使用pdf解析器时遇到了类似的问题。最初我用iteratee包(它supports random access)。 AFAIK它是唯一具有随机IO支持的IO库。

current approach基于io-streams包。我发现它更方便。性能足够货,attoparsec开箱即用,包括很多combinators。

这是一个基本的例子,如何使用iteratee随机IO:

[email protected]:/tmp/shum$ cat test.hs 

import qualified Data.Iteratee as I 
import qualified Data.Attoparsec.Iteratee as I 
import qualified Data.Attoparsec.Char8 as P 
import Control.Monad.IO.Class 
import System.Environment 

main :: IO() 
main = do 
    [file] <- getArgs 
    flip I.fileDriverRandom file $ do 
    I.seek 20 
    num1 <- I.parserToIteratee P.number 
    liftIO $ print num1 
    I.seek 10 
    num2 <- I.parserToIteratee P.number 
    liftIO $ print num2 
[email protected]:/tmp/shum$ cat in.data 
111111111 
222222222 
333333333 
[email protected]:/tmp/shum$ runhaskell test.hs in.data 
333333333 
222222222 
[email protected]:/tmp/shum$ 
+0

我对iteratee技术并不熟悉。最近我收集了很多人的兴趣,也许这是解决我的问题的好方法,但是iteratee包本身对我来说有点太稀疏了,无法解决这个问题。 – MathematicalOrchid 2013-02-24 15:33:02

+0

我加了一个例子。这是非常基本的,但应该给出这个想法。 – Yuras 2013-02-24 15:50:13