2012-05-09 8 views
9

小さなファイルから何かを読み込み、変更して、同じファイルに書き戻すデーモンを作成しています。私はそれを書き込もうとする前に、各ファイルが読んだ後すぐに閉じられることを確認する必要があります。私はまた、すぐにもう一度読むことがあるので、各ファイルが執筆後すぐに閉じられるようにする必要があります。すぐにファイルが閉じられるようにする

私はバイナリの代わりにバイナリstrictを使用しましたが、厳密なGetを提供するように見えます。 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 

答えて

7

は、conduitpipesiteratee又はenumeratorとしてパッケージを使用することを検討してください。遅延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 
+0

恥知らずのプラグイン: 'パイプ'は、約1週間以内に迅速かつ決定論的で構成可能な資源管理を出そろうとしています。 –

+0

@GabrielGonzalez優れています。それがリリースされたら私にpingし、私はこの答えを更新します。 –

+1

完了です。私は[reddit](http://www.reddit.com/r/haskell/comments/txkb0/pipes_20_pipe_finalization/)で発表しました。 –

11

ファイルへの「puts」または「writes」はすべて厳密です。 writeFileの行為は、ディスク上に置くためにすべてのハスケルデータを評価することを要求します。

だから、あなたが集中する必要があるのは入力の怠惰な読書です。あなたの上のあなたの例では、両方のファイルを怠け者に読んだ後、遅れてデコードします。

代わりに、ファイルを厳密に(厳密なバイトコードで)試してみると、問題ありません。

+0

私は混乱しています。私は ''私は 'doStuff'のサンクに最初に縛られていて、私たちが怠惰な' readFile'を使ったので実際にIOは起こっていないと想像します。しかし、いったん 'print i'を実行すると、' i'の評価とすべてのIOの完了を強制しませんか? '解凍 'はファイルのすべてを読み取っていないので、開いたままにしていますか? – pat

+0

ドン、あなたの説明は私がなぜ「puts」や「writes」の厳密なバージョンを必要としないのか理解するのを助けました。有難うございます。しかし、私は解凍の厳密なバージョンを見つける必要もあると思います。最終的に私はコードに従うのが少し楽になったので、Nathanのソリューションに行きました。 – mhwombat

2

ら導管を使用する代わりに。 System.IOを使用するだけです。これにより、IO実行順序に関してファイルが閉じられたときに明示的に制御することができます。

あなたは、あなたがそれで終わったら、通常の読み取り操作に続いてopenBinaryFileData.ByteStringからおそらくもの)とhCloseを使用するか、またはwithBinaryFile、自動的にファイルを閉じる(しかしthis sort of problemを注意してください)することができます。

あなたが使用している方法が何であれ、Donが言ったように、厳密なbytestringとして読んでから、strictをfromChunksで遅延して変換したいと思うでしょう。

関連する問題