trz*_*zek 6 heap inputstream clojure
我之前发布了一个巨大的XML文件 - 它是一个287GB的XML,带有维基百科转储我想要放入CSV文件(修订作者和时间戳).我设法做到这一点,直到某一点.之前,我得到了StackOverflow的错误,但现在解决第一个问题后,我得到:java.lang.OutOfMemoryError:Java堆空间错误.
我的代码(部分取自Justin Kramer的答案)看起来像这样:
(defn process-pages
[page]
(let [title (article-title page)
revisions (filter #(= :revision (:tag %)) (:content page))]
(for [revision revisions]
(let [user (revision-user revision)
time (revision-timestamp revision)]
(spit "files/data.csv"
(str "\"" time "\";\"" user "\";\"" title "\"\n" )
:append true)))))
(defn open-file
[file-name]
(let [rdr (BufferedReader. (FileReader. file-name))]
(->> (:content (data.xml/parse rdr :coalescing false))
(filter #(= :page (:tag %)))
(map process-pages))))
Run Code Online (Sandbox Code Playgroud)
我没有表现article-title,revision-user和revision-title功能,因为他们只是简单地从网页或修改散列特定场所需要的数据.任何人都可以帮助我 - 我在Clojure中真的很新,并没有遇到问题.
需要明确的是,(:content (data.xml/parse rdr :coalescing false))我很懒。如果您不相信,请检查其类别或拉出第一个项目(它将立即返回)。
也就是说,在处理大型序列时需要注意以下几点:抓住头部,以及未实现/嵌套的惰性。我认为你的代码受到后者的影响。
这是我的建议:
1) 添加(dorun)到调用链的末尾->>。这将迫使序列完全实现,而无需抓住头部。
2)更改for为process-page. doseq你正在向文件吐口水,这是一个副作用,你不想在这里懒惰地这样做。
正如 Arthur 建议的那样,您可能希望打开一个输出文件一次并继续写入它,而不是为每个维基百科条目打开并写入(吐出)。
更新:
这是一个重写,试图更清楚地分离关注点:
(defn filter-tag [tag xml]
(filter #(= tag (:tag %)) xml))
;; lazy
(defn revision-seq [xml]
(for [page (filter-tag :page (:content xml))
:let [title (article-title page)]
revision (filter-tag :revision (:content page))
:let [user (revision-user revision)
time (revision-timestamp revision)]]
[time user title]))
;; eager
(defn transform [in out]
(with-open [r (io/input-stream in)
w (io/writer out)]
(binding [*out* out]
(let [xml (data.xml/parse r :coalescing false)]
(doseq [[time user title] (revision-seq xml)]
(println (str "\"" time "\";\"" user "\";\"" title "\"\n")))))))
(transform "dump.xml" "data.csv")
Run Code Online (Sandbox Code Playgroud)
我在这里没有看到任何会导致内存使用过多的内容。