确保文件立即关闭

时间:2012-05-09 14:21:35

标签: file haskell io strict

我正在编写一个守护程序,它从一个小文件读取内容,修改它,然后将其写回同一个文件。在尝试写入之前,我需要确保每个文件在阅读后立即关闭。我还需要确保每个文件在写完后立即关闭,因为我可能偶尔会立即再次阅读它。

我已经研究过使用二进制严格而不是二进制,但似乎只提供严格的Get,而不是严格的Put。与System.IO.Strict相同的问题。从阅读二进制严格的文档,我不确定它真的解决了我确保文件被迅速关闭的问题。处理这个问题的最佳方法是什么? DeepSeq?

这是一个高度简化的示例,可以让您了解我的应用程序的结构。此示例以

结尾
*** Exception: test.dat: openBinaryFile: resource busy (file is locked)

原因很明显。

import Data.Binary ( Binary, encode, decode )
import Data.ByteString.Lazy as B ( readFile, writeFile )
import Codec.Compression.GZip ( compress, decompress )

encodeAndCompressFile :: Binary a => FilePath -> a -> IO ()
encodeAndCompressFile f = B.writeFile f . compress . encode

decodeAndDecompressFile :: Binary a => FilePath -> IO a
decodeAndDecompressFile f = return . decode . decompress =<< B.readFile f

main = do
  let i = 0 :: Int
  encodeAndCompressFile "test.dat" i
  doStuff

doStuff = do
  i <- decodeAndDecompressFile "test.dat" :: IO Int
  print i
  encodeAndCompressFile "test.dat" (i+1)
  doStuff

3 个答案:

答案 0 :(得分:11)

对文件的所有“放置”或“写入”都是严格的。 writeFile的行为要求评估所有Haskell数据,以便将其放在磁盘上。

所以你需要关注的是输入的懒惰阅读。在上面的例子中,你懒得读取文件,然后懒洋洋地解码它。

相反,请尝试严格阅读文件(例如使用严格的字节串),你会没事的。

答案 1 :(得分:7)

考虑使用conduitpipesiterateeenumerator等软件包。它们提供了懒惰IO(更简单的代码,可能更小的内存占用)的许多好处,而没有惰性IO。以下是使用conduitcereal的示例:

import Data.Conduit
import Data.Conduit.Binary (sinkFile, sourceFile)
import Data.Conduit.Cereal (sinkGet, sourcePut)
import Data.Conduit.Zlib (gzip, ungzip)
import Data.Serialize (Serialize, get, put)

encodeAndCompressFile :: Serialize a => FilePath -> a -> IO ()
encodeAndCompressFile f v =
  runResourceT $ sourcePut (put v) $$ gzip =$ sinkFile f

decodeAndDecompressFile :: Serialize a => FilePath -> IO a
decodeAndDecompressFile f = do
  val <- runResourceT $ sourceFile f $$ ungzip =$ sinkGet get
  case val of
    Right v  -> return v
    Left err -> fail err

main = do
  let i = 0 :: Int
  encodeAndCompressFile "test.dat" i
  doStuff

doStuff = do
  i <- decodeAndDecompressFile "test.dat" :: IO Int
  print i
  encodeAndCompressFile "test.dat" (i+1)
  doStuff

答案 2 :(得分:2)

使用导管等的替代方案。将只使用System.IO,这将允许您明确控制文件何时关闭IO执行顺序。

您可以使用openBinaryFile,然后使用正常阅读操作(可能是Data.ByteString}和hClose完成后的操作,或withBinaryFile,关闭文件自动(但要注意this sort of problem)。

无论你使用哪种方法,正如唐所说,你可能想要读作严格的字节串,然后用fromChunks将严格转换为懒惰。