2012-10-26 22 views
3

我有一個二進制文件,其中包含X by X矩陣。文件本身是一系列單精度浮點數(小端)。我想要做的是解析它,並將其填入合理的clojure矩陣數據類型。解析一個little-endian二進制文件,填充到矩陣中

感謝this question,我看到我可以用光澤解析二進制文件。我現在看起來像這樣的代碼:

(ns foo.core 
    (:require gloss.core) 
    (:require gloss.io) 
    (:use [clojure.java.io]) 
    (:use [clojure.math.numeric-tower])) 

(gloss.core/defcodec mycodec 
    (gloss.core/repeated :float32 :prefix :none)) 

(def buffer (byte-array (* 1200 1200))) 

(.read (input-stream "/path/to/binaryfile") buffer) 

(gloss.io/decode mycodec buffer) 

這需要一段時間才能運行,但最終會拋出一大串數字。不幸的是,這些數字都是錯誤的。經進一步調查,數字被視爲大端。

假設有一些方法可以將這些二進制文件作爲小端文件讀取,我想將結果填充到矩陣中。 This question似乎已經解決了使用Incanter並行Colt代表的問題,但是,這個問題是從'09,我希望堅持clojure 1.4和lein 2.在我瘋狂的谷歌搜索中,我看到了其他的建議jblas或mahout。這些天有沒有「最好」的clojure矩陣庫?

編輯:讀取二進制文件非常接近。由於這個方便nio wrapper,我能得到一個內存映射的字節緩衝區爲短的一行,甚至重新排序:

(ns foo.core 
    (:require [clojure.java.io :as io]) 
    (:require [nio.core :as nio]) 
    (:import [java.nio ByteOrder])) 

(def buffer (nio/mmap "/path/to/binaryfile")) 

(class buffer) ;; java.nio.DirectByteBuffer 

(.order buffer java.nio.ByteOrder/LITTLE_ENDIAN) 
;; #<DirectByteBuffer java.nio.DirectByteBuffer[pos=0 lim=5760000 cap=5760000]> 

然而,重新排序而不做中間體(DEF)步,失敗:

(.order (nio/mmap f) java.nio.ByteOrder/LITTLE_ENDIAN) 

;; clojure.lang.Compiler$CompilerException: java.lang.IllegalArgumentException: Unable to resolve classname: MappedByteBuffer, compiling:(/Users/peter/Developer/foo/src/foo/core.clj:12) 
;; at clojure.lang.Compiler.analyzeSeq (Compiler.java:6462) 
;;  clojure.lang.Compiler.analyze (Compiler.java:6262) 
;; etc... 

我想能夠創建重新排序的字節緩衝這裏面的函數沒有定義一個全局變量,但現在它似乎不喜歡那樣。另外,一旦我把它重新排序,我不完全確定如何處理我的DirectByteBuffer,因爲它似乎不可迭代。也許對於閱讀這個緩衝區對象(進入JBLAS矩陣)的剩餘步驟,我會創建第二個問題。

編輯2:我在接受下面的答案,因爲我認爲我原來的問題結合了太多的東西。一旦我找出剩下的部分,我會嘗試用完整的代碼來更新這個問題,該代碼以這個ByteBuffer開始,並讀入JBLAS矩陣(這看起來是正確的數據結構)。

如果有人感興趣,我能夠創建一個返回正常有序的ByteBuffer如下功能:

;; This works! 
(defn readf [^String file] 
    (.order 
    (.map 
    (.getChannel 
    (java.io.RandomAccessFile. file "r")) 
    java.nio.channels.FileChannel$MapMode/READ_ONLY 0 (* 1200 1200)) 
    java.nio.ByteOrder/LITTLE_ENDIAN)) 

NIO的包裝,我發現長相簡化/美化這個相當多,但它會出現我要麼沒有正確使用它,要麼出現了問題。爲了與NIO包裝回顧一下我的發現:

;; this works 
(def buffer (nio/mmap "/bin/file")) 
(def buffer (.order buffer java.nio.ByteOrder/LITTLE_ENDIAN)) 
(def buffer (.asFloatBuffer buffer)) 

;; this fails 
(def buffer 
    (.asFloatBuffer 
    (.order 
    (nio/mmap "/bin/file") 
    java.nio.ByteOrder/LITTLE_ENDIAN))) 

可悲的是,這是另一天一個Clojure的奧祕,或者其他的StackOverflow問題。

+0

至於以不同的順序調用它。我不知道。如果你不使用clojure nio包裝,你會得到嗎? – Bill

回答

2

打開FileChannel(),然後得到一個內存映射緩衝區。網上有很多關於這一步的教程。

通過調用order(endian-ness)(不是無參數order)將緩衝區的順序切換到小端。最後,提取浮點數的最簡單方法是調用asFloatBuffer()並使用結果緩衝區讀取浮點數。

之後,你可以把數據放到你需要的任何結構中。

編輯下面是如何使用API​​的示例。

;; first, I created a 96 byte file, then I started the repl 
;; put some little endian floats in the file and close it 
user=> (def file (java.io.RandomAccessFile. "foo.floats", "rw")) 
#'user/file 
user=> (def channel (.getChannel file)) 
#'user/channel 
user=> (def buffer (.map channel java.nio.channels.FileChannel$MapMode/READ_WRITE 0 96)) 
#'user/buffer 
user=> (.order buffer java.nio.ByteOrder/LITTLE_ENDIAN) 
#<DirectByteBuffer java.nio.DirectByteBuffer[pos=0 lim=96 cap=96]> 
user=> (def fbuffer (.asFloatBuffer buffer)) 
#'user/fbuffer 
user=> (.put fbuffer 0 0.0) 
#<DirectFloatBufferU java.nio.DirectFloatBufferU[pos=0 lim=24 cap=24]> 
user=> (.put fbuffer 1 1.0) 
#<DirectFloatBufferU java.nio.DirectFloatBufferU[pos=0 lim=24 cap=24]> 
user=> (.put fbuffer 2 2.3) 
#<DirectFloatBufferU java.nio.DirectFloatBufferU[pos=0 lim=24 cap=24]> 
user=> (.close channel) 
nil 

;; memory map the file, try reading the floats w/o changing the endianness of the buffer 
user=> (def file2 (java.io.RandomAccessFile. "foo.floats" "r")) 
#'user/file2 
user=> (def channel2 (.getChannel file2))             
#'user/channel2 
user=> (def buffer2 (.map channel2 java.nio.channels.FileChannel$MapMode/READ_ONLY 0 96)) 
#'user/buffer2 
user=> (def fbuffer2 (.asFloatBuffer buffer2)) 
#'user/fbuffer2 
user=> (.get fbuffer2 0) 
0.0 
user=> (.get fbuffer2 1) 
4.6006E-41 
user=> (.get fbuffer2 2) 
4.1694193E-8 

;; change the order of the buffer and read the floats  
user=> (.order buffer2 java.nio.ByteOrder/LITTLE_ENDIAN)         
#<DirectByteBufferR java.nio.DirectByteBufferR[pos=0 lim=96 cap=96]> 
user=> (def fbuffer2 (.asFloatBuffer buffer2)) 
#'user/fbuffer2 
user=> (.get fbuffer2 0) 
0.0 
user=> (.get fbuffer2 1) 
1.0 
user=> (.get fbuffer2 2) 
2.3 
user=> (.close channel2) 
nil 
user=> 
+0

我正確的告訴我使用nio嗎?經過一番挖掘,我發現這個頁面(https://github.com/pjstadig/nio)看起來像是包裝了一些這些功能。將做更多的挖掘,看看我能否解決這個問題。 – Peter

+0

是的。你說對了。我會盡量在當天晚些時候添加一個例子。 – Bill

+0

嗯,我得到了一個DirectByteBuffer,因爲'(nio/mmap(nio/channel(io/file「/ path/to/file」)))'。現在要弄清楚順序。另外,對於Java和Clojure來說,感覺有些厚重。 – Peter