Haskell - 读取整个 Lazy ByteString

Haskell - Reading entire Lazy ByteString

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

现在要执行某些操作,我已经定义了对同一文件进行操作的某些函数,因此我想打开、读取文件并将其转换为 Xlsx 一次,并将其保存在内存中以对其进行操作。

现在我正在读取文件 bs <- Data.ByteString.Lazy.readfile file 并在最后做 Data.ByteString.Lazy.length bs 'seq' return value.

有什么方法可以使用这个功能,将文件作为一个整体保存在内存中,以便重复使用吗?

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

例如,如果您 运行 对大文件执行以下程序:

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 从每个计算的操作系统缓存,并在所有计算中将其保存在内存中。但是,如果您 运行 对此代码进行一些堆分析,您会发现第一个操作将整个文件加载到 "pinned" 字节串中,并且该分配在后续操作中保持不变。

如果您担心的是希望在开始时读取完整的文件,即使第一个操作不需要全部读取,这样就不会因为文件的其他部分而导致后续延迟阅读,那么您基于 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)