5

複数のファイルを単一の ByteString として一定のメモリで遅延して読み取るにはどうすればよいですか?

readFiles :: [FilePath] -> IO ByteString

現在、次の実装がありますが、プロファイリングから見たものと私の理解からn-1、メモリ内のファイルで終了します。

readFiles = foldl1 joinIOStrings . map ByteString.readFile
    where joinIOStrings ml mr = do
                                l <- ml
                                r <- mr
                                return $ l `ByteString.append` r

foldl1 joinIOStringsここでの欠陥は、IO アクションを適用してからそれらを再ラップしていることであることを理解しています。そのため、それらを適用せずに を置き換える方法が必要だと思います。

4

2 に答える 2

7

複数のファイルを単一の ByteString として一定のメモリで遅延して読み取るにはどうすればよいですか?

一定のメモリ使用量が必要な場合は、Data.ByteString.Lazy. strictByteStringは遅延して読み取ることができず、O(sum of filesizes)メモリを必要とします。

ファイル数がそれほど多くない場合は、単純にすべてをD.B.L.readFile読み取り (遅延読み取り)、結果を連結するのが適切です。

import qualified Data.ByteString.Lazy as L

readFiles :: [FilePath] -> IO L.ByteString
readFiles = fmap L.concat . mapM L.readFile

mapM L.readFileファイルを開きますが、要求されたときにのみ各ファイルの内容を読み取ります。

ファイルの数が多く、OS が 1 つのプロセスで許可する開いているファイル ハンドルの制限を使い果たす可能性がある場合は、さらに複雑な処理が必要になります。の独自の遅延バージョンを作成できますmapM

import System.IO.Unsafe (unsafeInterleaveIO)

mapM_lazy :: [IO a] -> IO [a]
mapM_lazy [] = return []
mapM_lazy (x:xs) = do
              r <- x
              rs <- unsafeInterleaveIO (mapM_lazy xs)
              return (r:rs)

各ファイルは、その内容が必要な場合にのみ開かれ、以前に読み取られたファイルが既に閉じられている場合にのみ開かれます。ハンドルを閉じる時間が保証されていないため、それでもリソースの制限に達する可能性がわずかにあります。

または、お気に入りiterateeの 、enumeratorconduitまたは体系的な方法で問題を解決するパッケージを使用できます。それぞれに長所と短所があり、正しくコーディングされていれば、誤ってリソース制限に達する可能性がなくなります。

于 2012-10-01T14:24:04.840 に答える
1

レイジーバイト文字列を使用していると思います(からData.ByteString.Lazy)。これを行うにはおそらく他の方法がありますが、1 つのオプションは単純に次のように使用することconcat :: [ByteString] -> ByteStringです。

import Control.Monad
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Lazy as ByteString

readFiles :: [FilePath] -> IO ByteString
readFiles = fmap ByteString.concat . mapM ByteString.readFile

(注:コードをテストする時間はありませんが、ドキュメントを読むと、これはうまくいくはずです)

于 2012-10-01T14:22:37.083 に答える