Haskell - 读取整个Lazy ByteString

时间:2018-02-27 18:19:58

标签: haskell lazy-evaluation bytestring

上下文:我在名为toXlsx :: ByteString -> Xlsx的库中定义了一个函数(ByteString来自Data.ByteString.Lazy)

现在要做某些操作我已经定义了在同一个文件上运行的某些函数,因此我想打开,读取并将文件转换为Xlsx一次并将其保存在内存中以便与它一起操作。

现在我正在以bs <- Data.ByteString.Lazy.readfile file的形式阅读该文件,最后正在Data.ByteString.Lazy.length bs 'seq' return value

有没有办法使用这个函数并将文件作为一个整体保存在内存中以重用它?

1 个答案:

答案 0 :(得分:4)

请注意,惰性字节字符串的工作方式,文件的内容在被“使用”之前不会被读取,但一旦被读取,它们将保留在内存中以用于任何后续操作。它们被从内存中删除的唯一方法是它们是垃圾收集的,因为你的程序不再有任何方法可以访问它们。

例如,如果您在大文件上运行以下程序:

import qualified Data.ByteString.Lazy as BL  
main = do
  bigFile <- BL.readFile "ubuntu-14.04-desktop-amd64.iso"
  print $ BL.length $ BL.filter (==0) bigFile     -- takes a while
  print $ BL.length $ BL.filter (==255) bigFile   -- runs fast

第一次计算实际上会将整个文件读入内存,并将保留在那里进行第二次计算。

我猜这本身并不太令人信服,因为操作系统也会将文件缓存到内存中,并且最终很难分辨出Haskell从操作系统缓存中读取文件的时间差异计算并将其保存在所有计算的内存中。但是,如果您对此代码运行了一些堆分析,您会发现第一个操作将整个文件加载到“固定”字节串中,并且该分配在后续操作中保持不变。

如果您担心的是您希望在开始时读取完整的文件,即使第一个操作不需要全部读取,因此在读取文件的其他部分时不会出现后续延迟,然后基于seq的解决方案可能没问题。或者,您可以将整个文件作为严格的字节串读取,然后使用fromStrict进行转换 - 此操作是即时的,不会复制任何数据。 (与toStrict相比,这是昂贵的, 复制数据。)所以这将有效:

import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL

main = do
  -- read strict
  bigFile <- BS.readFile "whatever.mov"
  -- do strict and lazy operations
  print $ strictOp bigFile
  print $ lazyOp (BL.fromStrict bigFile)