• Advertisement
Sign in to follow this  

C++ Binary Parsing - ifstream reading different types

This topic is 1899 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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 );
}

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites

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



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

Share this post


Link to post
Share on other sites
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! :)

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement