【发布时间】:2013-04-29 23:40:57
【问题描述】:
我想使用 Clojure 从维基词典 XML 转储中提取标题。
我使用head -n10000 > out-10000.xml 创建原始怪物文件的较小版本。然后我用文本编辑器进行了修剪以使其成为有效的 XML。我根据里面的行数重命名了文件(wc -l):
(def data-9764 "data/wiktionary-en-9764.xml") ; 354K
(def data-99224 "data/wiktionary-en-99224.xml") ; 4.1M
(def data-995066 "data/wiktionary-en-995066.xml") ; 34M
(def data-7999931 "data/wiktionary-en-7999931.xml") ; 222M
以下是 XML 结构的概述:
<mediawiki>
<page>
<title>dictionary</title>
<revision>
<id>20100608</id>
<parentid>20056528</parentid>
<timestamp>2013-04-06T01:14:29Z</timestamp>
<text xml:space="preserve">
...
</text>
</revision>
</page>
</mediawiki>
这是我尝试过的,基于this answer to 'Clojure XML Parsing':
(ns example.core
(:use [clojure.data.zip.xml :only (attr text xml->)])
(:require [clojure.xml :as xml]
[clojure.zip :as zip]))
(defn titles
"Extract titles from +filename+"
[filename]
(let [xml (xml/parse filename)
zipped (zip/xml-zip xml)]
(xml-> zipped :page :title text)))
(count (titles data-9764))
; 38
(count (titles data-99224))
; 779
(count (titles data-995066))
; 5172
(count (titles data-7999931))
; OutOfMemoryError Java heap space java.util.Arrays.copyOfRange (Arrays.java:3209)
我的代码做错了吗?或者这可能是我正在使用的库中的错误或限制?基于 REPL 实验,我使用的代码似乎是惰性的。在下面,Clojure 使用了一个 SAX XML 解析器,因此仅此一项应该不是问题。
另见:
2013 年 4 月 30 日更新:
我想分享一些来自 clojure IRC 频道的讨论。我在下面粘贴了一个经过编辑的版本。 (我删除了用户名,但如果你想知道,请告诉我;我会编辑并给你一个链接。)
整个标签在
xml/parse中一次性读入内存, 早在你打电话给计数之前。clojure.xml使用 ~lazy SAX 解析器生成一个急切的具体集合。懒惰地处理 XML 需要做的工作比你想象的要多得多——这将是工作你 做,不是什么魔法clojure.xml可以为你做的。随意反驳 致电(count (xml/parse data-whatever))。
总而言之,即使在使用zip/xml-zip 之前,这个xml/parse 也会导致OutOfMemoryError 具有足够大的文件:
(count (xml/parse filename))
目前,我正在探索其他 XML 处理选项。在我的列表顶部是clojure.data.xml,如https://stackoverflow.com/a/9946054/109618 所述。
【问题讨论】:
-
啊,是的。早该发现的。你肯定想要
clojure.data.xml而不是clojure.xml- 过渡应该很容易。
标签: xml clojure out-of-memory