writing binary values (bytes) to a file

1,781 views
Skip to first unread message

prhlava

unread,
Nov 19, 2008, 11:51:08 AM11/19/08
to Clojure

Hello all,

I have started to play with the clojure a day ago and today I have
almost finished porting simple program (from plt-scheme).

The place I got stuck in is - how to write a binary value (multiple
bytes) in one write operation to a file... The code uses
java.io.FileWriter, but this wants string.

What I need to write is 3 RHS bytes from an integer, (the pix is
Integer in following code):

(. ofile write (str
(char (bit-shift-right pix 16))
(char (bit-shift-right pix 8))
(char pix)))
)

writes wrong stuff. Is this unicode thing, my noob status thing or
something else?

Kind regards,

Vlad

pmf

unread,
Nov 19, 2008, 1:42:56 PM11/19/08
to Clojure
On Nov 19, 5:51 pm, prhlava <prhl...@googlemail.com> wrote:
> (. ofile write (str
>                                            (char (bit-shift-right pix 16))
>                                            (char (bit-shift-right pix 8))
>                                            (char pix)))
>                            )
>

Dealing with byte-arrays, you will want to use the write-method that
takes an array, not the one that takes a string.

Constructing the array is a bit tricky:

; Define your int
(def pix 652187261)

; Define your array, typed as array of char
(def payload
(into-array Character/TYPE [(char (bit-shift-right pix 16))
(char (bit-shift-right pix 8))
(char pix)]))

; Create your FileWriter
(def ofile (java.io.FileWriter. "somefile.bin"))

; Write and close
(.write ofile payload)
(.close ofile)

wwmorgan

unread,
Nov 19, 2008, 2:58:24 PM11/19/08
to Clojure
In general, Writers are for character data and OutputStreams are for
binary data. If possible, create a FileOutputStream like this:

(ns my.ns
(:import (java.io FileOutputStream)))

(def ofile (FileOutputStream. "/some/file/somewhere"))

and use one of the write methods of FileOutputStream.

Graham Fawcett

unread,
Nov 19, 2008, 3:22:36 PM11/19/08
to clo...@googlegroups.com
Hi,

> Dealing with byte-arrays, you will want to use the write-method that
> takes an array, not the one that takes a string.
>
> Constructing the array is a bit tricky:
>
> ; Define your int
> (def pix 652187261)
>
> ; Define your array, typed as array of char
> (def payload
> (into-array Character/TYPE [(char (bit-shift-right pix 16))
> (char (bit-shift-right pix 8))
> (char pix)]))

Char's on the JVM are not bytes, and are wider than 8 bits -- so if
you're packing binary data, you should use Byte and byte instead of
Character and char. (Note too that the pix value is greater than 2^24,
so you need four bytes to store it if you're using a byte-array.)

As wwmorgan mentioned, FileOutputStreams can write out byte-buffers,
so the following seems to work:

; Define your int
(def pix 652187261)

; Define your array, typed as array of byte (not char!)
(def payload
(into-array Byte/TYPE [(byte (bit-shift-right pix 24))
(byte (bit-shift-right pix 16))
(byte (bit-shift-right pix 8))
(byte pix)]))

; Create your FileWriter
(def ofile (java.io.FileOutputStream. "/tmp/somefile.bin2"))

; Write and close
(.write ofile payload)
(.close ofile)

The created file contains four bytes: 26 df 96 7d.

Bonus question for the bored reader: write a function,
(byte-array-maker N), that takes a width N, and returns a function
that takes an Integer as input and returns an N-width byte array
containing the Integer in big-endian order. E.g.

((byte-array-maker 4) 652187261)
=> [4-byte array: 26, df, 96, 7d]

Cheers,
Graham

prhlava

unread,
Nov 19, 2008, 3:45:44 PM11/19/08
to Clojure

Hello again,

Thank you all for the posts and explanations,

After getting the clojure SVN version and few tweaks in the code, the
working result looks like:

(with-open [ofile (new java.io.FileOutputStream
(str result-directory
"/"
(make-filename x y))
(boolean true))] ; I am appending only
(. ofile write
(into-array Byte/TYPE
[(byte (bit-shift-right pix 16))
(byte (bit-shift-right pix 8))
(byte pix)])) ; the pix is of the Integer type

Vlad

Mark Volkmann

unread,
Nov 19, 2008, 4:49:28 PM11/19/08
to clo...@googlegroups.com

It looks like with-open accepts any number of bindings. Does it just
call close on the first one when the body finishes or on all of them?

If I wanted to figure this out for myself, how would I find the source
code for with-open?

--
R. Mark Volkmann
Object Computing, Inc.

Stuart Halloway

unread,
Nov 19, 2008, 5:09:00 PM11/19/08
to clo...@googlegroups.com
Hi Mark,

The metadata points to the source:

user> (meta #'with-open)
{:doc "bindings => name init\n\n Evaluates body in a try expression
with name bound to the value of\n init, and a finally clause that
calls (.close name).", :ns #<Namespace clojure.core>, :arglists
([bindings & body]), :file "core.clj", :name with-open, :macro
true, :line 1752}

If you look through the archive I believe somebody posted code that
uses the metadata to locate the source and display it at the REPL.

Stu

Graham Fawcett

unread,
Nov 19, 2008, 5:53:28 PM11/19/08
to clo...@googlegroups.com

Hi,

If you're using Slime in Emacs, type in the word "with-open" (or put
your cursor on it in a Clojure source file) and press M-. (alt-period
or Escape, period).

Best,
Graham

Mark Volkmann

unread,
Nov 19, 2008, 5:55:35 PM11/19/08
to clo...@googlegroups.com
Thanks Stu! I wasn't aware of the meta function. That helps a lot!

I looked for occurrences of "(meta " throughout the clojure and
clojure-contrib repositories, but I didn't find a function that prints
the code for a given function. That would be incredibly useful for
learning! Does anybody know what function does that and whether it is
part of clojure-contrib?

Jeff Bester

unread,
Nov 19, 2008, 8:32:03 PM11/19/08
to Clojure
Not sure if this will help with what you are working on. I ran across
a similar problem last week and ended up writing a generic library
that converts various sizes of integers to big or little endian byte
arrays and then back again.

Code is located at: http://github.com/jbester/cljext/tree/master/cljext%2Fbinpack.clj

Jeffrey

prhlava

unread,
Nov 20, 2008, 5:36:01 PM11/20/08
to Clojure

Hello Graham,

> Bonus question for the bored reader: write a function,
> (byte-array-maker N), that takes a width N, and returns a function
> that takes an Integer as input and returns an N-width byte array
> containing the Integer in big-endian order. E.g.
>
> ((byte-array-maker 4) 652187261)
> => [4-byte array: 26, df, 96, 7d]

:-) writing the function thought me quite a bit :-)

Vlad

prhlava

unread,
Nov 21, 2008, 7:21:42 PM11/21/08
to Clojure

Hello Jeffrey,
Thanks for the link, more food for study on how to write a generic
library...

Basically, I am scheme/lisp noob, learning it in my spare time, the
progress is slow but rewarding...

Few observations about my own experience with starting with lisp:

1. concentrate on meaning and what fuctions do, not on the form and
"(((())))"
2. name your functions well
3. use tools to have your code idented properly
4. learn ways of creating abstracions and recognising when one is
asking to be written
(this thread kickstarted 4.)

Kind regards,

Vlad

Graham Fawcett

unread,
Nov 21, 2008, 7:53:07 PM11/21/08
to clo...@googlegroups.com
Hi Vlad,
Then my work here is done. ;-) I'm glad you took the challenge!

Best,
Graham



>
> Vlad
> >
>
Reply all
Reply to author
Forward
0 new messages