3

我有一个包含 X x X 矩阵的二进制文件。文件本身是一系列单精度浮点数(小端序)。我想做的是解析它,并将其填充到一些合理的 clojure 矩阵数据类型中。

感谢这个问题,我看到我可以用光泽解析二进制文件。我现在有如下代码:

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

这需要一段时间才能运行,但最终会输出一大串数字。不幸的是,这些数字都是错误的。经过进一步调查,这些数字被解读为大端序。

假设有某种方法可以将这些二进制文件作为小端读取,我想将结果填充到矩阵中。这个问题似乎已经决定使用 Incanter 及其 Parallel 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 矩阵(这似乎是正确的数据结构)。

如果有人感兴趣,我可以创建一个返回正确排序的字节缓冲区的函数,如下所示:

;; 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 问题。

4

1 回答 1

2

打开 a 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=> 
于 2012-10-26T04:12:19.023 回答