【问题标题】:Ensuring files are closed promptly确保文件及时关闭
【发布时间】:2012-05-18 02:00:58
【问题描述】:

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

我研究过使用二进制严格而不是二进制,但似乎只提供严格的 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

【问题讨论】:

    标签: file haskell io strict


    【解决方案1】:

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

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

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

    【讨论】:

    • 我很困惑。我想i 最初绑定到doStuff 中的一个thunk,并且自从我们使用惰性readFile 以来实际上没有发生任何IO。但是,一旦我们print i,那不是强制评估i,并完成所有IO吗? decompress 没有读取所有文件,所以它保持打开状态吗?
    • 唐,您的解释帮助我理解了为什么我不需要严格版本的“puts”或“writes”;谢谢你。但是,我想我还需要找到一个严格的解压缩版本。最终我选择了 Nathan 的解决方案,因为我发现遵循代码更容易一些。
    【解决方案2】:

    考虑使用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
    

    【讨论】:

    • 无耻插件:pipes 即将在一周内推出快速、确定性和可组合的资源管理。
    • @GabrielGonzalez 非常棒。当它发布时给我打电话,我会更新这个答案。
    • 完成了。我刚刚在reddit 上宣布了它。
    【解决方案3】:

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

    您可以使用openBinaryFile,然后使用正常的读取操作(可能是来自Data.ByteString 的操作)和hClose,当您完成它时,或者withBinaryFile,它会自动关闭文件(但要注意@987654323 @)。

    无论您使用什么方法,正如 Don 所说,您可能希望读取为严格的字节串,然后使用 fromChunks 将严格转换为惰性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-08-23
      • 2019-02-16
      • 1970-01-01
      • 1970-01-01
      • 2021-11-03
      • 2019-02-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多