C++ Binary Parsing - ifstream reading different types

Started by
8 comments, last by mynameisnafe 11 years, 4 months ago
Hi Guys,

I've got some silly questions about some basic IO stuff I should probably know to be fair.

I'm using ifstream to access a binary file for read.

My question is, how do I read different types, using fstream? Am I stuck to char arrays?

It's a 3ds file, so it's hierarchically chunked, with headers and bodies. I need to be able to read x amount of bytes, and convert it to a given type, i.e. an unsigned short or an unsigned int, or an array of floats, or even chars, say.

Can I use something like the following?


void*
Parser::temp_buffer;
// ..read two bytes into the buffer.. somehow without passing a char*
// then..
chunk->ID = (unsigned short) temp_buffer ;


Or do I just have to wrap up a bunch of FILE* and fread functionality to do this?

Also, how do I store the current file pointer position as a member of a static class?

Thanks in advance :)
Advertisement
istream::read and istream::seekg are what you're looking for:

unsigned short id;
in.read((char*)&id, sizeof(id));


You can cut down the amount of repetitive code by using a template function:

template<class T>
istream& read_binary( istream& in, T& val ) {
in.read( (char*)&val, sizeof(val) );
return in;
}

void test(istream& file) {
short id;
read_binary( file, id );
}
Be aware that istreams are formatted input streams.Trying to use one to read unformatted data is a bit offlabel.

Yu might want to pull from a file using a std::streambuf instead, it's job is to pull data from a file and make it available as a stream of bytes.

Stephen M. Webb
Professional Free Software Developer


istream::read and istream::seekg are what you're looking for:

unsigned short id;
in.read((char*)&id, sizeof(id));


You can cut down the amount of repetitive code by using a template function:

template<class T>
istream& read_binary( istream& in, T& val ) {
in.read( (char*)&val, sizeof(val) );
return in;
}

void test(istream& file) {
short id;
read_binary( file, id );
}



that looks G! Thank you :)
Okay, that template function is sweet! Thanks :)

I've now got a question about std::cout. I'm printing out some info for debugging (ID, SIZE, mSTART, END), like so:


static void PrintChunk( BinaryFileMapChunk* chunk ) {
std::cout
<< "\n Type: " << std::hex << chunk->TYPE << std::endl;
std::cout
<< "\n Size: " << (unsigned int) chunk->SIZE
<< "\n Start Pos: " << (unsigned int) chunk->START
<< "\n End Pos: " << (unsigned int) chunk->END;
}



The issue is this line:
std::cout << "\n Type: " << std::hex << chunk->TYPE << std::endl

It seems that this line makes all my output come out as hex, if I remove the std::hex itall comes out fine except the ID value which I'd like to see as hex isn't hex so it's not very useful to me..

What's going on?
std::hex keeps in effect until you stick in another base field formatter. So if you want to go back to decimal stick in a << std::dec before the next number you want to be in decimal.
I find that the 'sticky' behaviour of std::hex is rarely something I want. I usually have something like this reinvented in every project I work on that makes use of iostreams:


#include <iostream>
#include <iomanip>


template<typename T>
struct as_hex_wrapper
{
as_hex_wrapper(T n) : n(n) { }
T n;
};

template<typename T>
as_hex_wrapper<T> as_hex(T n) { return as_hex_wrapper<T>(n); }

template<typename T>
std::ostream &operator<< (std::ostream &out, const as_hex_wrapper<T> &ahw)
{
// TODO: should really restore previous base and showbase settings
return out << std::hex << std::showbase << ahw.n << std::noshowbase << std::dec;
}

// -------------

int main()
{
const unsigned n = 0xDeadBeef;
std::cout << as_hex(n) << std::endl;
return 0;
}


std::showbase gives you the leading "0x", which I almost always want when displaying anything other than a stream of bytes in hex.
Thats some cool looking code - I'm slowly getting exposed to more stuff like this as_hex_wrapper(T n) : n(n) { }.

I decided to use std::hex and std::dec in my print function, as it's only there to debug with. Thanks :)

The next issue I've run into is what to do when you know the next 'few' bytes are a string but you don't know how long that string is..

Do I need the string.. potentially, it's not uber important, but I may as well. My question is, how do I read bytes until the byte is a null character?

Thanks again in advance,

Nathan

My question is, how do I read bytes until the byte is a null character?



std::string s;
std::getline(the_stream, s, '\0');
Hi there,

I fixed it with this:


static std::string ReadString( int pos )
{
std::string ret;
int count = 0;
char val = ' ';
inputStream.seekg( pos, std::ios::beg );
while( val != '\0' )
{
ReadChunkPart( inputStream, val );
ret.append( 1, val );
count++;
}
current_pointer_position = inputStream.tellg();
return ret;
}


I'm doing pretty well actually. I've chewed through the files chunks and found the data I need, and I've figured that way of getting strings.

The only question left for me to ask I think is this:

Given this template function, which reads the value of a type into a given instance of said type..


template<class T>
istream& read_binary( istream& in, T& val ) {
in.read( (char*)&val, sizeof(val) );
return in;
}


How do I modify it to read in a given amount of a type into an array of said type? Something like below?


template<class T>
T * read_binary_array( istream& in, int count ) {

T * tmp = new T[ count ];

in.read( (char*) &tmp, count * sizeof(T) );

return tmp;

}


Thanks again, you guys have been great! :)

This topic is closed to new replies.

Advertisement