在Haskell中处理真正大型二进制文件的最有效方法是什么?
标准答案是将整个文件作为惰性ByteString读取,然后使用Binary数据包之类的东西来编写解析器。这有几个问题...
首先,像Binary这样的库并没有真正处理解析失败,而且我明确地期待解析有时失败。
其次,我没有解析整个文件内容。我将跳过它的大块。并且将数十亿字节的数据从磁盘读入RAM只是为了让垃圾收集器再次将其丢弃,这似乎相当不合理。
与此相关,我需要能够判断我想要执行的跳过是否会将文件从文件的末尾带走(如果出现错误则将错误输出)。
我可能还需要查找向后,或者可能需要查找文件中的特定字节偏移量,这似乎没有得到懒惰的ByteString方法的良好支持。 (最终将整个文件保存在RAM中存在严重危险。)
当然,另一种方法是逐个读取单个字节,与hSeek
命令交错。但现在的问题是,一次读取一个字节的文件效率如何?听起来它可能也非常慢。我不确定hSetBuffering
会对此产生影响。 (?)
然后当然有mmap
。但是,如果在大文件上使用虚拟内存系统,那似乎就会吓坏。 (这很奇怪,考虑到它存在的全部目的......)
伙计们,我们怎么想?在I / O性能和代码可维护性方面,最好的方法是什么?
答案 0 :(得分:2)
在处理pdf解析器时我遇到了类似的问题。最初我使用了iteratee
包(supports random access)。 AFAIK它是唯一具有随机IO支持的IO库。
我的current approach基于io-streams
包。我发现它更方便。性能就足够了,attoparsec
整合开箱即用,包括很多组合。
以下是如何将iteratee
用于随机IO的基本示例:
shum@shum-laptop:/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
shum@shum-laptop:/tmp/shum$ cat in.data
111111111
222222222
333333333
shum@shum-laptop:/tmp/shum$ runhaskell test.hs in.data
333333333
222222222
shum@shum-laptop:/tmp/shum$