Ben*_*err 5 io file-io haskell lazy-evaluation lazy-sequences
如何使用常量内存懒惰地将多个文件作为单个ByteString读取?
readFiles :: [FilePath] -> IO ByteString
Run Code Online (Sandbox Code Playgroud)
我目前有以下实现,但从我从分析中看到的以及我的理解,我将以n-1
内存中的文件结束.
readFiles = foldl1 joinIOStrings . map ByteString.readFile
where joinIOStrings ml mr = do
l <- ml
r <- mr
return $ l `ByteString.append` r
Run Code Online (Sandbox Code Playgroud)
我知道这里的缺陷是我正在应用IO操作然后重新打包它们所以我认为我需要的是一种替换它foldl1 joinIOStrings
而不应用它们的方法.
如何使用常量内存懒惰地将多个文件作为单个ByteString读取?
如果你想要恒定的内存使用,你需要Data.ByteString.Lazy
.一个严格的ByteString
懒惰不能读,并需要O(sum of filesizes)
记忆.
对于不太多的文件,只需读取它们(D.B.L.readFile
懒惰地读取)并连接结果就可以了,
import qualified Data.ByteString.Lazy as L
readFiles :: [FilePath] -> IO L.ByteString
readFiles = fmap L.concat . mapM L.readFile
Run Code Online (Sandbox Code Playgroud)
在mapM L.readFile
将打开文件,但只有它被要求当读取每个文件的内容.
如果文件数量很大,那么操作系统允许单个进程允许的打开文件句柄限制可能会耗尽,您需要更复杂的东西.你可以煮自己的懒人版mapM
,
import System.IO.Unsafe (unsafeInterleaveIO)
mapM_lazy :: [IO a] -> IO [a]
mapM_lazy [] = return []
mapM_lazy (x:xs) = do
r <- x
rs <- unsafeInterleaveIO (mapM_lazy xs)
return (r:rs)
Run Code Online (Sandbox Code Playgroud)
这样,只有当需要其内容时才会打开每个文件,而以前读取的文件已经可以关闭.由于关闭句柄的时间无法保证,因此仍有可能仍然遇到资源限制.
或者您可以使用自己喜欢的iteratee
,enumerator
,conduit
或任何包,解决了系统的方式的问题.它们中的每一个都有其自身的优点和缺点,如果编码正确,则消除了意外达到资源限制的可能性.