У меня есть каталог с около 4500 XML (HTML5) файлами, и я хочу создать «манифест» их данных (по существу title
и base/@href
).Обработка (тоже) многих XML-файлов (с TagSoup)
С этой целью я использовал функцию для сбора всех соответствующих путей к файлу, открывая их с помощью readFile, отправив их в парсер, основанный на tagoup, и затем вывел/форматировал результирующий список.
Это работает для подмножества файлов, но в итоге работает в ошибке openFile: resource exhausted (Too many open files)
. После некоторого чтения это не так удивительно: я использую mapM parseMetaDataFile files
, который сразу открывает все ручки.
Что я не могу понять, так это, как обойти проблему. Я пробовал прочитать немного об Итерате; Можно ли легко подключить это с помощью тегов? Строгий IO, так как я использовал его в любом случае (хе-хе), заморозил мой компьютер, хотя файлы не очень большие (в среднем 28 КБ).
Любые указатели были бы весьма благодарны. Я понимаю, что подход к созданию большого списка может также потерпеть неудачу, но 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'). –
Мне бы хотелось обработать их по одному файлу за раз! Я не знаю, как это сделать, хотя ... – vicvicvic