qer*_*oip 6 clojure binary-data
如何在Clojure中处理大型二进制数据文件?假设数据/文件大约为50MB - 小到足以在内存中处理(但没有简单的实现).
以下代码正确地从小文件中删除^ M,但它会抛出OutOfMemoryError
较大的文件(如6MB):
(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
Run Code Online (Sandbox Code Playgroud)
似乎Java字节数组不能被视为seq,因为它非常低效.
在另一方面,提供解决方案aset
,aget
并且areduce
是臃肿,丑陋的和必要的,因为你不能真正使用Clojure的序列库.
我错过了什么?如何在Clojure中处理大型二进制数据文件?
我可能会在这里亲自使用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))))
Run Code Online (Sandbox Code Playgroud)
请注意,这与您的代码基本相同,但构造一个单独的延迟序列来计算更改的字节数.