我想解析一个大的 json 文件 (3GB) 并返回该文件中每一行的 HashMap 。我的直觉是使用转换器逐行处理文件,并用一些选定的字段(> 文件中字节的 5%)构造一个向量。
但是,以下代码会抛出 OutOfMemory 异常:
文件.json
{"experiments": {"results": ...}}
{"experiments": {"results": ...}}
{"experiments": {"results": ...}}
解析器.clj
(defn load-with!
"Load a file using a parser, a structure and a transducer."
[parser structure xform path]
(with-open [r (clojure.java.io/reader path)]
(into structure xform (parser r))))
(def xf (map #(get-in % ["experiments" "results"])))
(def parser (comp (partial map cheshire.core/parse-string) line-seq))
(load-with! parser (vector) xf "file.json")
当我使用 JVisualVM 可视化进程时,堆会随着时间的推移而增长,并在进程崩溃之前超过 25 GB。
传感器适合这种情况吗?有更好的选择吗?
我的要求之一是在函数末尾返回新结构。因此,我无法使用doseq就地处理文件。
此外,我需要根据文件格式更改解析器和转换器。
谢谢!
最佳答案
你已经很接近了。我不知道 json/parse-string
的作用,但它是否与 here 中的 json/read-str
相同那么这段代码应该就是你想要做的事情。
看起来你想要这样的东西:
(require '[clojure.data.json :as json])
(require '[clojure.java.io :as java])
(defn load-with!
"Load a file using a parser, a structure and a transducer."
[parser structure xform path]
(with-open [r (java/reader path)]
(into structure (xform (parser r)))))
(def xf (partial map #(get-in % ["experiments" "results"])))
(def parser (comp (partial map json/read-str) line-seq))
(load-with! parser [] xf "file.json")
我猜这些只是将所有业务细节删除到这里的最小示例中所犯的错误。使用下面的代码我能够处理一个大文件,上面的代码给了我一个 OOM 错误:
(require '[clojure.data.json :as json])
(require '[clojure.java.io :as java])
(def structure (atom []))
(defn do-it! [xform path]
(with-open [r (java/reader path)]
(doseq [line (line-seq r)]
(swap! structure conj (xform line)))))
(defn xf [line]
(-> (json/read-str line)
(get-in ["experiments" "results"])))
(do-it! xf "file.json")
(take 10 @structure)
关于clojure - 使用 Clojure Transducers 解析大文件 : OutOfMemory Error,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40193129/