Xml 读取和关闭Haskell中的文件

Xml 读取和关闭Haskell中的文件,xml,haskell,file-io,Xml,Haskell,File Io,我是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 =

我是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 
searchXML::String->IO新闻
searchXML文件=do
新闻组
searchForKW关键字=do
xmlList takeExtension p==“.xml”)”
xml新闻->Bool
kwInNews关键字(新闻{author=a,headline=b,content=c})=isInfixOf关键字c
但是,这会导致
openFile:资源耗尽(打开的文件太多)
错误。所以我认为这些文件是打开阅读的,而不是关闭的。我怎样才能解决这个问题


PS:任何进一步的重构提示都是非常受欢迎的。

readFile函数就是因为这个而臭名昭著的。它假装将整个文件读入一个巨大的字符串,但实际上并没有。只需打开文件进行读取并立即返回即可。只有在以下任一情况下,文件才会关闭:

  • 程序检查字符串的最后一个字符
  • 程序会删除对结果的所有引用,垃圾回收器就会运行
问题是,哈斯克尔很懒。看起来您的代码可能会立即处理整个字符串,但实际上这取决于您如何处理该处理的结果。这类事情很难搞清楚。Haskell的全部观点是,代码何时实际执行并不重要,但我们需要在特定时刻执行代码,因为只有在代码运行时才发生真实世界中可观察到的事情

实际上,
readFile
非常适合快速检查一些小示例是否按照您的预期工作。只要您想要控制文件的打开/关闭时间,或者想要高性能(即处理大型XML文件),您就需要避免
readFile


如果您知道文件很小/性能不重要,您可以手动
openFile
hGetLine
hClose
。这样,您就可以确切地知道文件何时关闭,因为您正在关闭它。您可能还想看看ByteString库;有一个类似于
readFile
的函数,它返回一个严格的ByteString(换句话说,它实际上一次加载整个文件)。
ByteString
类型也比
String
类型有效得多(但使用起来更灵活)。

根据@MathematicalOrchid的建议,我正在使用
readFile
from Data.ByteString和
Data.ByteString.Char8.unpack
函数将ByteString转换为字符串

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 " " 
导入符合条件的数据.ByteString作为Str
将限定的Data.ByteString.Char8导入为Char8
searchXML::String->IO新闻
searchXML文件=do

rsp问题是您读取文件时比较懒惰-最好使用类似于为什么要将其转换为
字符串
<事实证明,code>String
几乎总是错误的。如果您使用
{-#OverloadedStrings#-}
您可以使用
ByteString
文本,
ByteString
提供了许多不同的stringy操作供您使用来处理它们。但是对于XML,有一些包可以正确地做到这一点。
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 " "