我是Haskell的初学者,目前我正在尝试解析xml文件列表。
要解析给定文件名中的xml文件,我使用以下函数
searchXML :: String -> IO News
searchXML file = do
rsp <- readFile file
let tags = parseTags rsp
return News { author = get_val "createdBy" tags,
headline = get_val "headline" tags,
content = get_val "text" tags}
where
extr a b c = drop 1 $ takeWhile (~/= TagClose a) $
dropWhile (~/= TagOpen a b) c
get a b = extr "value" [] $ extr "property" [("name",a)] b
get_val a b = fromTagText $ (get a b) !! 0
调用xml文件列表
searchForKW :: String -> IO [News]
searchForKW keyword = do
xmlList <- simpleFind (\p -> takeExtension p == ".xml") "."
xml <- mapM searchXML xmlList
return $ filter (kwInNews keyword) xml
where
kwInNews :: String -> News -> Bool
kwInNews keyword (News {author=a,headline=b,content=c}) = isInfixOf keyword c
然而,这会导致openFile: resource exhausted (Too many open files)
错误。所以我认为文件是打开阅读但没有关闭。我怎样才能解决这个问题?
PS:非常欢迎任何进一步的重构提示。
答案 0 :(得分:2)
readFile
函数因此而臭名昭着。它假装将整个文件读成一个巨大的字符串,但事实并非如此。只需打开文件进行阅读即可立即返回。该文件只有在以下情况下才会关闭:
麻烦的是,Haskell很懒。它可能看起来就像您的代码立即处理整个字符串一样,但实际上它取决于您对该处理结果的处理方式。弄清楚这种事情可能相当棘手。 Haskell的整个点是你的代码实际执行时应该无关紧要 - 但我们在这里,需要代码在特定时刻执行,因为现实世界的可观察事物只发生在代码运行。
实际上,readFile
非常适合快速检查一些小例子如何运作。只要您想要控制文件何时打开/关闭或者您想要高性能(即处理大型 XML文件),您就要避免readFile
。
如果您知道文件较小/性能不重要,则可以手动openFile
,hGetLine
和hClose
。这样你就知道文件何时关闭,因为你正在关闭它。您可能还想查看ByteString库;有一个类似于readFile
的函数,它返回一个严格的ByteString(换句话说,它真的一次加载整个文件)。 ByteString
类型的效率也比String
类型高得多(但使用起来更加繁琐)。
答案 1 :(得分:0)
遵循@MathematicalOrchid的建议我使用Data.ByteString中的readFile
和Data.ByteString.Char8.unpack
函数将ByteString转换为String
import qualified Data.ByteString as Str
import qualified Data.ByteString.Char8 as Char8
searchXML :: String -> IO News
searchXML file = do
rsp <- Str.readFile file
let get a = getVal a $ parseTags $ Char8.unpack rsp
auth = get "createdBy"
headl = get "headline"
cont = get "text"
return News {author = auth, headline = headl, content = cont}
where
extract tag attr = (drop 1) . (takeWhile (~/= TagClose tag))
. dropWhile (~/= TagOpen tag attr)
getVal attr = (fromTagText . safeHead . extract "value" [])
. extract "property" [("name", attr)]
safeHead (x:xs) = x
safeHead [] = TagText " "