如何使用Data.ByteString解析7GB文件?

时间:2012-04-04 13:28:22

标签: parsing haskell readfile bytestring

我必须解析一个文件,确实必须首先阅读它,这是我的程序:

import qualified Data.ByteString.Char8 as B
import System.Environment    

main = do
 args      <- getArgs
 let path  =  args !! 0
 content   <- B.readFile path
 let lines = B.lines content
 foobar lines 

 foobar :: [B.ByteString] -> IO()
 foobar _ = return ()

但是,在编译之后

> ghc --make -O2 tmp.hs 

使用7G文件调用时,执行会发生以下错误。

> ./tmp  big_big_file.dat
> tmp: {handle: big_big_file.dat}: hGet: illegal ByteString size (-1501792951): illegal operation

感谢您的回复!

2 个答案:

答案 0 :(得分:9)

ByteString的长度为Int。如果Int为32位,则7GB文件将超出Int的范围,缓冲区请求的大小将错误,并且可以轻松请求负大小。

readFile的代码将文件大小转换为缓存请求的Int

readFile :: FilePath -> IO ByteString
readFile f = bracket (openBinaryFile f ReadMode) hClose
    (\h -> hFileSize h >>= hGet h . fromIntegral)

如果溢出,“非法ByteString大小”错误或分段错误是最可能的结果。

如果可能,请使用lazy ByteString来处理大文件。在您的情况下,您几乎必须使其成为可能,因为使用32位Int s,无法创建7GB ByteString

如果您需要对行进行严格ByteString的行,并且没有行非常长,您可以通过惰性ByteString来实现

import qualified Data.ByteString.Lazy.Char8 as LC
import qualified Data.ByteString.Char8 as C

main = do
    ...
    content <- LC.readFile path
    let llns = LC.lines content
        slns = map (C.concat . LC.toChunks) llns
    foobar slns

但如果您可以修改处理懒惰ByteString的处理,那么总体上可能会更好。

答案 1 :(得分:5)

严格ByteString仅支持最多2 GiB的内存。您需要使用lazy ByteStrings才能生效。