我有一个包含大约4500个XML(HTML5)文件的目录,我想创建其数据的“清单”(实质上为title
和base/@href
)。处理(太)许多XML文件(使用TagSoup)
为此,我一直在使用函数来收集所有相关的文件路径,使用readFile打开它们,将它们发送到基于标签的解析器,然后输出/格式化结果列表。
这适用于文件的子集,但最终会遇到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')一次处理一个文件。 – 2011-05-09 22:38:42
我很乐意一次处理一个文件!我不知道如何做到这一点,虽然... – vicvicvic 2011-05-09 22:55:16