9

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

我已经研究过使用二进制严格而不是二进制,但似乎只提供严格的 Get,而不是严格的 Put。System.IO.Strict 也有同样的问题。通过阅读二进制严格文档,我不确定它是否真的解决了我确保文件被及时关闭的问题。处理这个问题的最佳方法是什么?深度序列?

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

*** 例外:test.dat:openBinaryFile:资源繁忙(文件被锁定)

出于显而易见的原因。

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
4

3 回答 3

11

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

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

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

于 2012-05-09T14:55:31.640 回答
7

考虑使用诸如管道管道迭代器枚举器之类的包。在没有惰性 IO 的情况下,它们提供了惰性 IO 的许多好处(代码更简单,内存占用可能更小)。这是一个使用导管谷物的示例:

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
于 2012-05-09T20:08:05.333 回答
2

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

您可以使用openBinaryFile后跟正常的读取操作(可能来自 的操作Data.ByteString)以及hClose完成后的操作,或者withBinaryFile,它会自动关闭文件(但要注意此类问题)。

无论您使用哪种方法,正如 Don 所说,您可能希望将其读取为严格的字节串,然后使用fromChunks.

于 2012-05-11T01:17:06.400 回答