2010-08-21 39 views
6

如何处理Clojure中的大型二进制数据文件?我们假设数据/文件大约为50MB--足够小,可以在内存中处理(但不是天真的实现)。如何在Clojure中处理大型二进制数据?

下面的代码正确删除^ 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 

似乎Java字节数组不能作为SEQ,因为它处理效率极低。

另一方面,aset,agetareduce的解决方案臃肿,丑陋和迫切,因为您不能真正使用Clojure序列库。

我错过了什么? 如何处理Clojure中的大型二进制数据文件?

回答

6

我可能会在这里亲自使用aget/aset/areduce - 它们可能是必要的,但在处理数组时它们是有用的工具,而且我不觉得它们特别难看。如果你想包装他们在一个很好的功能,那么当然你可以:-)

如果你决定使用序列,那么你的问题将在seq的构造和遍历,因为这将需要创建和存储为数组中的每个字节创建一个新的seq对象。这大概是每个阵列字节~24字节......

所以诀窍是让它工作懒惰,在这种情况下,先前的对象将被垃圾收集,然后再到达结束阵列。但是为了实现这个功能,当你遍历序列时(例如计数),你必须避免引用seq的头部。

下可能工作(未经测试),但将取决于写斌文件在一个慵懒友好的方式来实现:

(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)))) 

注意,这是本质上是相同的代码,而是构建了一个单独的延迟序列来计算更改的字节数。

+1

谢谢!按照你的建议,懒惰做了这个诀窍。 总之,在Clojure中处理二进制文件: **使用懒惰相对比较简单,并且占用的内存很少,但是它非常低效。关键是永远不会意识到整个序列。 **使用循环/ recur + aset/aget/areduce/amap势在必行,内存占用少且速度更快。这可能是做到这一点的“正确”方式。在我的具体情况下,我应该实施一些举措。 – qertoip 2010-08-22 08:22:04