2010-08-21 80 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