如何在Clojure中处理大型二进制数据文件?假设数据/文件大约为50MB - 小到足以在内存中处理(但没有简单的实现)。
以下代码正确地从小文件中删除了^ M,但是对于较大的文件(例如6MB),它会抛出OutOfMemoryError
:
(defn read-bin-file [file]
(to-byte-array (as-file file)))
(defn remove-cr-from-file [file]
(let [dirty-bytes (read-bin-file file)
clean-bytes (filter #(not (= 13 %)) dirty-bytes)
changed? (< (count clean-bytes) (alength dirty-bytes))] ; OutOfMemoryError
(if changed?
(write-bin-file file clean-bytes)))) ; writing works fine
似乎 Java字节数组不能被视为seq ,因为效率极低。
另一方面,aset
,aget
和areduce
的解决方案变得臃肿,丑陋且势在必行,因为您无法真正使用Clojure序列库。
我错过了什么? 如何在Clojure中处理大型二进制数据文件?
答案 0 :(得分:6)
我个人可能会在这里使用aget / aset / areduce - 它们可能是必要的但它们在处理数组时是很有用的工具,我觉得它们并不特别难看。如果你想将它们包装在一个很好的功能中,那么你当然可以: - )
如果您决定使用序列,那么您的问题将在seq的构造和遍历中,因为这将需要为数组中的每个字节创建和存储新的seq对象。对于每个数组字节,这可能是~24个字节......
所以诀窍就是让它懒散地运行,在这种情况下,在你到达数组末尾之前,早期的对象将被垃圾收集。但是,要使其工作,您必须避免在遍历序列时保持对seq头部的任何引用(例如,使用count)。
以下可能有效(未经测试),但将依赖于以惰性友好方式实现的write-bin文件:
(defn remove-cr-from-file [file]
(let [dirty-bytes (read-bin-file file)
clean-bytes (filter #(not (= 13 %)) dirty-bytes)
changed-bytes (count (filter #(not (= 13 %)) dirty-bytes))
changed? (< changed-bytes (alength dirty-bytes))]
(if changed?
(write-bin-file file clean-bytes))))
请注意,这与您的代码基本相同,但构造一个单独的延迟序列来计算更改的字节数。