2

title約 4500 個の XML (HTML5) ファイルを含むディレクトリがあり、それらのデータ (基本的にはと)の「マニフェスト」を作成したいと考えていますbase/@href

この目的のために、関数を使用して関連するすべてのファイル パスを収集し、それらを readFile で開き、tagsoup ベースのパーサーに送信してから、結果のリストを出力/フォーマットしています。

これはファイルのサブセットに対して機能しますが、最終的にopenFile: resource exhausted (Too many open files)エラーが発生します。少し読んだ後では、これはそれほど驚くべきことではありません。私はこれを使用しmapM parseMetaDataFile filesて、すべてのハンドルをすぐに開きます。

私が理解できないのは、問題を回避する方法です。Iteratee について少し読んでみました。これを Tagsoup で簡単に接続できますか? Strict IO は、とにかく私が使用した方法 (へー) では、ファイルがそれほど大きくなくても (平均で 28 KB)、コンピューターをフリーズさせました。

任意のポインタをいただければ幸いです。大きなリストを作成するアプローチも失敗する可能性があることは理解していますが、4.5k 要素はそれほど長くはありません...また、どこにでもあるはずStringですByteString

ここにいくつかのコードがあります。素朴なことをお詫びします:

import System.FilePath
import Text.HTML.TagSoup

data MetaData = MetaData String String deriving (Show, Eq)

-- | Given HTML input, produces a MetaData structure of its essentials.
-- Should obviously account for errors, but simplified here.
readMetaData :: String -> MetaData
readMetaData input = MetaData title base
 where
  title =
    innerText $
    (takeWhile (~/= TagClose "title") . dropWhile (~/= TagOpen "title" []))
    tags
  base = fromAttrib "href" $ head $ dropWhile (~/= TagOpen "base" []) tags
  tags = parseTags input

-- | Parses MetaData from a file.
parseMetaDataFile :: FilePath -> IO MetaData
parseMetaDataFile path = fmap readMetaData $ readFile path

-- | From a given root, gets the FilePaths of the files we are interested in.
-- Not implemented here.
getHtmlFilePaths :: FilePath -> IO [FilePath]
getHtmlFilePaths root = undefined

main :: IO
main = do
  -- Will call openFile for every file, which gives too many open files.
  metas <- mapM parseMetaDataFile =<< getHtmlFilePaths

  -- Do stuff with metas, which will cause files to actually be read.
4

2 に答える 2

3

手っ取り早い解決策:

parseMetaDataFile path = withFile path $ \h -> do
    res@(MetaData x y) <- fmap readMetaData $ hGetContents h
    Control.Exception.evaluate (length (x ++ y))
    return res

もう少し良い解決策は、単に評価を使用するのではなく、の適切なNFDataインスタンスを作成することです。MetaData

于 2011-05-09T22:43:57.083 に答える
2

現在の設計を保持したい場合は、返す前に parseMetaDataFile が readFile からの文字列全体を消費していることを確認する必要があります。readFile がファイルの終わりに達すると、ファイル記述子は閉じられます。

于 2011-05-09T22:43:06.687 に答える