Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/12.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

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.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
使用data.zip解析Clojure中的XML时发生OutOfMemoryError_Xml_Clojure_Out Of Memory - Fatal编程技术网

使用data.zip解析Clojure中的XML时发生OutOfMemoryError

使用data.zip解析Clojure中的XML时发生OutOfMemoryError,xml,clojure,out-of-memory,Xml,Clojure,Out Of Memory,我想使用Clojure从wiktionaryxml转储中提取标题 我使用head-n10000>out-10000.xml创建原始monster文件的较小版本。然后我用一个文本编辑器进行修剪,使其成为有效的XML。我根据里面的行数重命名了文件(wc-l): 以下是XML结构的概述: <mediawiki> <page> <title>dictionary</title> <revision> <id

我想使用Clojure从wiktionaryxml转储中提取标题

我使用
head-n10000>out-10000.xml
创建原始monster文件的较小版本。然后我用一个文本编辑器进行修剪,使其成为有效的XML。我根据里面的行数重命名了文件(
wc-l
):

以下是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>
我的代码是否有问题?或者这可能是我正在使用的库中的一个bug或限制?基于REPL实验,我使用的代码似乎很懒。在底层,Clojure使用一个SAXXML解析器,因此这本身不应该是问题所在

另见:

更新2013-04-30:

我想分享clojure IRC频道的一些讨论。我在下面粘贴了一个经过编辑的版本。(我删除了用户名,但如果您想要积分,请告诉我;我会编辑并给您一个链接。)

整个标记在
xml/parse
中一次读入内存, 早在你打电话给伯爵之前。并且
clojure.xml
使用~lazy-SAX 解析器生成一个渴望的具体集合。惰性地处理XML 需要做的工作比你想象的要多得多——这将是你的工作 但是,不要用任何魔法
clojure.xml
来帮助你。请随便反驳 通过调用
(count(xml/parse data which))

总之,即使在使用
zip/xml-zip
之前,此
xml/parse
会导致文件足够大的
outofmemory错误

(count (xml/parse filename))
目前,我正在探索其他XML处理选项。在我的列表顶部,如中所述。

看一下,它似乎并不完全是懒惰的:

注意
(应用向量子项)
,它将
子项
序列具体化为一个向量(尽管它没有具体化整个子代树,所以它仍然是惰性的)。如果一个节点有大量的子节点(例如,
的子节点),那么即使是这一级别的惰性也不够--
:内容也需要是一个seq

我对拉链的了解非常有限,所以我不确定这里为什么使用
vector
;查看将
(关联节点:内容(和子节点(应用向量子节点)))
替换为
(关联节点:内容子节点)
是否有效,这样应该将
子节点保持为正常序列,而不会具体化它

(就此而言,我不知道为什么
(应用向量子项)
而不是
(向量子项)
。)

看起来它正在构建
*contents*
中的所有内容元素,因此OOM的源可能在内容处理程序本身中

我不确定我们如何协调拉链接口(树状)与您想要的流媒体。它适用于大型xml,但不适用于大型xml

在其他语言(例如Python)中的类似方法中,树是像zipper一样迭代构建的。不同之处在于,在成功处理元素后,树将被修剪

例如,在使用iterparse的Python中,您将在
页面
上侦听endElement事件(即当XML中出现
时)。此时,您知道您有一个完整的页面元素,可以将其作为树进行处理。完成后,删除刚刚处理的元素和控制内存使用的同级分支

也许您也可以在这里采用这种方法。xml拉链提供的节点是
xml/element
的变量。内容处理程序可以返回一个函数,该函数在调用时对其
*当前*
变量进行清理。然后你可以叫它来修剪这棵树


或者,您可以在clojure中将SAX“手动”用于根元素,并在遇到每个
页面
元素时为其创建一个拉链。

这是拉链数据结构的一个限制。拉链设计用于高效导航各种树,支持在树层次结构中上/下/左/右移动,并在几乎恒定的时间内进行就地编辑

从树中的任何位置,拉链都需要能够重新构造原始树(应用编辑)。为此,它将跟踪当前节点、父节点以及树中当前节点左右两侧的所有同级节点,大量使用持久数据结构

您正在使用的过滤器函数从节点最左边的子节点开始,一个接一个地工作到右边,同时测试谓词。最左边的子对象的拉链从其左边的同级对象的空向量开始(请注意源代码中的
:l[]
部分)。每次向右移动时,它都会将最后访问的节点添加到左侧同级的向量中(
:l(conj l node)
in)。当您到达最右边的子级时,您已经建立了树中该级别中所有节点的内存向量,对于像您这样的宽树,这可能会导致OOM错误

作为一种解决方法,如果您知道顶级元素只是一个包含
元素列表的容器,我建议使用拉链在页面元素中导航,只需使用
map
来处理页面:

(defn titles
  "Extract titles from +filename+"
  [filename]
  (let [xml (xml/parse filename)]
    (map #(xml-> (zip/xml-zip %) :title text)
         (:content xml))))

因此,基本上,我们避免对整个xml输入的顶层使用zip抽象,从而避免将整个xml保存在内存中。这意味着,对于更大的xml,其中每个第一级的子级都是巨大的,我们可能不得不在xml结构的第二级中再次跳过拉链,以此类推……

不确定向量是否严格必要,但我认为这不是OOM错误的原因。向量用于生成节点函数,该函数仅在以某种方式编辑拉链时调用。这里似乎不是这样。啊,是的。应该发现
(count (xml/parse filename))
(defn xml-zip
  "Returns a zipper for xml elements (as from xml/parse),
  given a root element"
  {:added "1.0"}
  [root]
    (zipper (complement string?) 
            (comp seq :content)
            (fn [node children]
              (assoc node :content (and children (apply vector children))))
            root))
(defn titles
  "Extract titles from +filename+"
  [filename]
  (let [xml (xml/parse filename)]
    (map #(xml-> (zip/xml-zip %) :title text)
         (:content xml))))