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

Fop*_*tin 7 parsing haskell bytestring readfile

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

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 ()
Run Code Online (Sandbox Code Playgroud)

但是,在汇编之后

> ghc --make -O2 tmp.hs 
Run Code Online (Sandbox Code Playgroud)

使用7Gigabyte文件调用时执行会发生以下错误.

> ./tmp  big_big_file.dat
> tmp: {handle: big_big_file.dat}: hGet: illegal ByteString size (-1501792951): illegal operation
Run Code Online (Sandbox Code Playgroud)

谢谢你的回复!

Dan*_*her 9

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

用于readFile将文件大小转换Int为缓冲区请求的代码

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

如果溢出,"非法字节串大小"错误或分段错误是最可能的结果.

如果可能的话,使用lazy ByteString来处理那些大的文件.在你的情况下,你几乎必须使它成为可能,因为32位Ints,7GB ByteString是不可能创建的.

如果您需要对行进行严格ByteString的行处理,并且没有行超长,那么您可以通过lazy 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
Run Code Online (Sandbox Code Playgroud)

但如果你可以修改你的处理来处理懒惰ByteString,那么整体可能会更好.


dfl*_*str 5

严格的ByteStrings仅支持最多2 GiB的内存.你需要使用懒惰ByteString工作.