我想使用xml-conduit
,特别Text.XML.Stream.Parse
是为了懒惰地从大型 XML 文件中提取对象列表。
作为测试用例,我使用了最近重新发布的 StackOverflow 数据转储。为简单起见,我打算从中提取所有用户名stackoverflow.com-Users.7z
。即使文件是 a .7z
,file
也表示它只是 bzip2 压缩的数据(文件末尾可能有一些 7zip 的东西,但现在我不在乎)。
XML 的简化版本是
<users>
<row id="1" DisplayName="StackOverflow"/>
...
<row id="2597135" DisplayName="Uli Köhler"/>
...
</users>
基于这个之前的问答和Hackage流的示例,以 bz2-ed 形式读取示例 XML 对我来说非常适合
但是,当runghc
用于运行以下程序时,它运行时不打印任何输出:
{-# LANGUAGE OverloadedStrings #-}
import Data.Conduit (runResourceT, ($$), ($=))
import qualified Data.Conduit.Binary as CB
import Data.Conduit.BZlib
import Data.Conduit
import Data.Text (Text)
import System.IO
import Text.XML.Stream.Parse
import Control.Applicative ((<*))
data User = User {name :: Text} deriving (Show)
parseUserRow = tagName "row" (requireAttr "DisplayName" <* ignoreAttrs) $ \displayName -> do
return $ User displayName
parseUsers = tagNoAttr "users" $ many parseUserRow
main = do
users <- runResourceT $ CB.sourceFile "stackoverflow.com-Users.7z" $= bunzip2 $= parseBytes def $$ force "users required" parseUsers
putStrLn $ unlines $ map show users
我认为出现此问题是因为 Haskell 尝试users
在开始打印之前深入评估列表。程序的内存使用量持续增长约每秒 2%(来源:htop)支持这一理论。
如何将结果“直播”到标准输出?我认为这可以通过$$ CB.sinkFile "output.txt"
在末尾添加另一个管道语句来实现。但是,此特定版本期望Conduit
输出ByteString
. 你能指出我从这里去哪里的正确方向吗?
任何帮助将不胜感激!