Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
处理(太)多XML文件(使用TagSoup)_Xml_Haskell_Io_Lazy Evaluation_Tag Soup - Fatal编程技术网

处理(太)多XML文件(使用TagSoup)

处理(太)多XML文件(使用TagSoup),xml,haskell,io,lazy-evaluation,tag-soup,Xml,Haskell,Io,Lazy Evaluation,Tag Soup,我有一个包含大约4500个XML(HTML5)文件的目录,我想创建它们数据的“清单”(基本上是title和base/@href) 为此,我一直在使用一个函数来收集所有相关的文件路径,用readFile打开它们,将它们发送到基于tagsoup的解析器中,然后输出/格式化结果列表 这适用于文件的一个子集,但最终会出现openFile:resource expensed(打开的文件太多)错误。在阅读了一些之后,这并不奇怪:我正在使用mapmparsemetadatafile文件,它可以直接打开所有句柄

我有一个包含大约4500个XML(HTML5)文件的目录,我想创建它们数据的“清单”(基本上是
title
base/@href

为此,我一直在使用一个函数来收集所有相关的文件路径,用readFile打开它们,将它们发送到基于tagsoup的解析器中,然后输出/格式化结果列表

这适用于文件的一个子集,但最终会出现
openFile:resource expensed(打开的文件太多)
错误。在阅读了一些之后,这并不奇怪:我正在使用
mapmparsemetadatafile文件
,它可以直接打开所有句柄

我不明白的是如何解决这个问题。我试着读了一些关于Iteratee的书;我能很容易地把它和塔格汤联系起来吗?严格IO,我使用它的方式(呵呵),冻结了我的电脑,即使文件不是很大(平均28 KB)

任何指点都将不胜感激。我意识到创建大列表的方法可能也会失败,但4.5k元素并没有那么长。。。此外,可能到处都应该有更少的
字符串
和更多的
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.
import System.FilePath
导入Text.HTML.TagSoup
数据元数据=元数据字符串派生(Show,Eq)
--|给定HTML输入,生成其基本内容的元数据结构。
--显然应该解释错误,但这里简化了。
readMetaData::字符串->元数据
readMetaData输入=元数据标题库
哪里
头衔=
内部文本$
(takeWhile(~/=TagClose“title”).dropWhile(~/=TagOpen“title”[]))
标签
base=fromAttrib“href”$head$dropWhile(~/=TagOpen“base”[])标记
tags=parseTags输入
--|解析文件中的元数据。
parseMetaDataFile::FilePath->IO元数据
parseMetaDataFile路径=fmap readMetaData$readFile路径
--|从给定根获取我们感兴趣的文件的文件路径。
--这里没有实现。
GetHtmlFilePath::FilePath->IO[FilePath]
GetHtmlFilePath根=未定义
main::IO
main=do
--将为每个文件调用openFile,这会导致打开的文件过多。

metas如果要保留当前设计,必须确保parseMetaDataFile在返回之前已使用readFile中的整个字符串。当readFile到达文件末尾时,文件描述符将被关闭。

快速而肮脏的解决方案:

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

res@(MetaData x y)您需要考虑您的设计,因为显然有太多的文件,您既不能同时打开它们的所有句柄(惰性方法),也不能同时打开和读取它们(完全严格的方法)。那么,使用严格的IO(例如,
Data.Text
)一次处理一个文件怎么样?我希望一次处理一个文件!我不知道怎么做,但是…有没有一个明显的方法可以做到这一点
readMetaData
从不使用整个文件;一旦它做了有趣的事情,我能“跳过消费”吗?@vicvic:看我的答案。在那里,一旦你得到你想要的东西,文件就被关闭了(通过
和file
)。啊,这是有效的,因为它强制计算x和y,从而强制获得文件的内容?我以前试过
使用file
,但是被对。。。所有内容(hGetContents将被调用得太迟)。@vicvic:是的--它确保您在关闭句柄之前已实际读取了所需的文件部分。刚刚测试了此解决方案,它的工作原理与我所希望的一样(处理所有文件大约需要4秒)。也许这很肮脏,但用纯语言解决IO问题也是如此,imho:)