約4500個のXML(HTML5)ファイルがあるディレクトリがあり、データのマニフェストを作成したいと考えています(実質title
とbase/@href
)。多くのXMLファイル(TagSoup付き)を扱っています
この目的のために、関連するすべてのファイルパスを収集し、それらをreadFileで開き、tagsoupベースのパーサーに送信し、結果リストを出力/書式設定する関数を使用しています。
これはファイルのサブセットで動作しますが、最終的にはopenFile: resource exhausted (Too many open files)
というエラーになります。いくつかの読書をした後、これはあまり驚くべきことではありません:私はmapM parseMetaDataFile files
を使用しています。すべてのハンドルをすぐに開きます。
私が理解できないことは、この問題を回避する方法です。私はIterateeについて少し読んでみました。 Tagsoupで簡単にそれをフックできますか?厳密なIOは、私がとにかく(heh)それを使用した方法は、ファイルは非常に(平均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.
あなたの設計について考える必要があります。明らかに、たくさんのファイルがあるので、すべてのハンドルを同時に開くことはできません(怠け者のアプローチ)。また、開いてそれらをすべて同時に読み込むことはできません(完全に厳密なアプローチ) 。では、厳密なIO(例: 'Data.Text')を使用して、一度に1つのファイルを処理する方法について説明します。 –
私は一度に1つのファイルを処理したいです!私はそれをどうやって行うのか分かりません... – vicvicvic