我有一个包含大约 4500 个 XML (HTML5) 文件的目录,我想为它们的数据创建一个“清单”(基本上是title
和base/@href
)。
为此,我一直在使用一个函数来收集所有相关的文件路径,用 readFile 打开它们,将它们发送到基于 tagoup 的解析器中,然后输出/格式化结果列表。
这适用于文件的子集,但最终会遇到openFile: resource exhausted (Too many open files)
错误。在做了一些阅读之后,这并不奇怪:我正在使用mapM parseMetaDataFile files
它立即打开所有手柄。
我不知道如何解决这个问题。我尝试阅读一些有关 Iteratee 的信息;我可以轻松地将其与 Tagsoup 挂钩吗?严格的 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.