如何在Clojure中将整个二进制文件(Nippy)读入字节数组?

7

我需要将存储在磁盘上的Nippy数据结构转换成可以被Nippy读取的格式。Nippy使用字节数组,因此我需要一种将文件转换为字节数组的方法。我已经尝试过

(clojure.java.io/to-byte-array (clojure.java.io/file folder-path file-path))

但这样会导致...
java.lang.IllegalArgumentException: Value out of range for byte: ? 

然后我尝试:

(into-array Byte/TYPE  (map byte (slurp (clojure.java.io/file folder-path file-path)))) 

但是某种原因导致命名空间错误,我找不到正确的命名空间。

为了首先编写Nippy结构,我正在使用:

(with-open [w (clojure.java.io/output-stream file-path)]
    (.write w (nippy/freeze data)))))
6个回答

33

以下是我如何使用Clojure内置函数来实现通用方法:

(defn slurp-bytes
  "Slurp the bytes from a slurpable thing"
  [x]
  (with-open [in (clojure.java.io/input-stream x)
              out (java.io.ByteArrayOutputStream.)]
    (clojure.java.io/copy in out)
    (.toByteArray out)))

编辑:根据Jerry101在评论中的建议更新了答案。


非常感谢您撰写这篇答案,它在我将图像转换为Base64 UTF8字符串的探索中帮助了我很多。 - asiegf
2
这很好,但输入流应该在 with-open 绑定中。您可以将输入和输出绑定在同一个 with-open 中。 - Jerry101

16

我不知道Clojure中是否有内置的方法可以处理这个问题。你绝对不想使用slurp,因为那会将流内容解码为文本。

你可以编写自己的方法来实现这一点,基本上是从InputStream读取到缓冲区,然后将缓冲区写入java.io.ByteArrayOutputStream。或者你可以使用Apache Commons IO的IOUtils

 (require '[clojure.java.io :as io])
 (import '[org.apache.commons.io IOUtils])

 (IOUtils/toByteArray (io/input-stream file-path))

你还应该看一下Nippy的thaw-from-in!freeze-to-out!函数:

 (import '[java.io DataInputStream DataOutputStream])

 (with-open [w (io/output-stream file-path)]
   (nippy/freeze-to-out! (DataOutputStream. w) some-data))

 (with-open [r (io/input-stream file-path)]
   (nippy/thaw-from-in! (DataInputStream. r)))

7

因为您知道文件的.length,所以可以一次性分配并使用DataInputStreamreadFully方法。不需要额外的库、缓冲区复制或循环。

(defn file-to-byte-array
  [^java.io.File file]
  (let [result (byte-array (.length file))]
    (with-open [in (java.io.DataInputStream. (clojure.java.io/input-stream file))]
      (.readFully in result))
    result))

2
一个快速的临时解决方案可能是这段代码:
(defn slurpb [is]
  "Convert an input stream is to byte array"
  (with-open [baos (java.io.ByteArrayOutputStream.)]
    (let [ba (byte-array 2000)]
      (loop [n (.read is ba 0 2000)]
        (when (> n 0)
          (.write baos ba 0 n)
          (recur (.read is ba 0 2000))))
      (.toByteArray baos))))

;;test
(String. (slurpb (java.io.ByteArrayInputStream. (.getBytes "hello"))))

2

0

你可以尝试使用ClojureWerk的Buffy:https://github.com/clojurewerkz/buffy

Buffy是一个Clojure库,用于处理二进制数据,在Clojure中编写完整的二进制协议实现,在离线缓存中存储复杂的数据结构,读取二进制文件以及执行通常使用ByteBuffer的所有操作。

如果你的二进制数据是有结构的,那么它非常方便,因为你可以定义复杂的组合类型和帧,这些类型和帧依赖于结构类型,甚至可以解码UTF。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接