如何在Clojure中处理大型二进制数据?

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中处理大型二进制数据文件?

mik*_*era 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))))
Run Code Online (Sandbox Code Playgroud)

请注意,这与您的代码基本相同,但构造一个单独的延迟序列来计算更改的字节数.