Thread overview
New to D: parse a binary file
Feb 05, 2011
scottrick
Feb 05, 2011
spir
Feb 05, 2011
bearophile
Feb 06, 2011
scottrick
Feb 06, 2011
bearophile
February 05, 2011
Hi,

I am new to D.  I am trying to write a binary file parser for a project of mine and I thought it would be fun to try and learn a new language at the same time.  So I chose D!  :D  I have been struggling however and have not been able to find very many good examples, so I am posting this message.

I think I'm supposed to be using std.stdio, but I'm not 100% sure. Could somebody post a short example of how to parse a couple of characters and ints or whatever from a file?  Or how to read, say, the next however many bytes into a struct?

Also, looking at the documentation, I am confused by this method signature:

T[] rawRead(T)(T[] buffer);

I understand that T is generic type, but I am not sure of the meaning of the (T) after the method name.

Thanks,
February 05, 2011
On 02/05/2011 06:26 PM, scottrick wrote:
> Hi,
>
> I am new to D.  I am trying to write a binary file parser for a
> project of mine and I thought it would be fun to try and learn a new
> language at the same time.  So I chose D!  :D  I have been
> struggling however and have not been able to find very many good
> examples, so I am posting this message.
>
> I think I'm supposed to be using std.stdio, but I'm not 100% sure.
> Could somebody post a short example of how to parse a couple of
> characters and ints or whatever from a file?  Or how to read, say,
> the next however many bytes into a struct?
>
> Also, looking at the documentation, I am confused by this method
> signature:
>
> T[] rawRead(T)(T[] buffer);
>
> I understand that T is generic type, but I am not sure of the
> meaning of the (T) after the method name.
>
> Thanks,

Below a pair of examples that should make all this clearer: a templated hand-written naive map func, and a template struct type (would be nearly the same for a class). Just run it. Additional explanations on demand.

import File=std.file;
import std.array;

Out[] map (In, Out) (In[] source, Out delegate (In) f) {
    // (0)
    Out[] target;
    foreach (element; source)
        target ~= f(element);
    return target;
}

struct StoreStack (T) {
    T[] items;
    string logFileName;
    this (string logFileName, T[] items=[]) {
        this.items = items;
        this.logFileName = logFileName;
        // create/reset log file
        File.write(logFileName, "");
    }
    string toString () {
        static form = "StoreStack(\"%s\", %s)";
        return format(form, this.logFileName, this.items);
    }

    void put (T item) {
        this.items ~= item;
        string message = format("put item: %s\n", item);
        File.append(logFileName, message);
    }
    T take () {
        T item = this.items[$-1];
        this.items = this.items[0..$-1];
        string message = format("took item: %s\n", item);
        File.append(logFileName, message);
        return item;
    }
}

unittest {
    // map
    string hex (uint i) { return format("0x%03X", i); }
    uint[] decs = [1, 3, 9, 27, 81, 243, 729];
    auto hexes = map!(uint,string)(decs, &hex);
    // auto hexes = map(decs, &hex);  // (1)
    writefln ("decs: %s\n-->\nhexes: %s", decs, hexes);
    writeln();

    // StoreStack
    auto store = StoreStack!(int)("test_log");
    // auto store = StoreStack!int("test_log");  // (2)
    store.put(3); store.put(2); store.put(3);
    auto i = store.take();
    writefln("store: %s", store);
    writefln("log:\n%s", File.readText("test_log"));
}
void main() {}

(0) The func must be declared as delegate (instead of simple func pointer) because: the actual func hex beeing defined in a block, the compiler turns it into a delegate. Detail.
(1) Here, the compiler is able to infer the template parameters (types): no need to specify them.
(2) When there is a single template parameter, the syntax allows omitting () around it.

Denis
-- 
_________________
vita es estrany
spir.wikidot.com

February 05, 2011
spir:

> Out[] map (In, Out) (In[] source, Out delegate (In) f) {
>      // (0)
...
>      string hex (uint i) { return format("0x%03X", i); }
>      uint[] decs = [1, 3, 9, 27, 81, 243, 729];
>      auto hexes = map!(uint,string)(decs, &hex);
...
> (0) The func must be declared as delegate (instead of simple func pointer) because: the actual func hex beeing defined in a block, the compiler turns it into a delegate. Detail.

See also:

void foo(In, Out)(Out function(In) f) {}
void main() {
     static int bar(int i) { return 0; }
     foo(&bar);
}

Bye,
bearophile
February 06, 2011
Thanks, your post was very helpful.  Two more questions (probably
related):

Where is the function 'format' defined?  Also, what is that 'unittest' block?  It compiles fine as is, but if I refer to format outside of unittest, it will not compile.  Also, if I compile and run your example, it doesn't do anything, since main() is empty?

Thanks again,
February 06, 2011
scottrick:

> Where is the function 'format' defined?

You need to add at the top of the module:
import std.conv: format;
Or:
import std.conv;


> Also, what is that 'unittest' block?  It compiles fine as is, but if I refer to format outside of
> unittest, it will not compile.  Also, if I compile and run your
> example, it doesn't do anything, since main() is empty?

It's an block of unit tests :-) Currently in your program they are not even compiled, so the format is not used. To run the unit tests you need to compile with -unittest compiler switch (with DMD).
See also:
http://www.digitalmars.com/d/2.0/unittest.html

Bye,
bearophile