Jump to content

  • Log In with Google      Sign In   
  • Create Account


C++ Binary Parsing - ifstream reading different types


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
9 replies to this topic

#1 mynameisnafe   Members   -  Reputation: 235

Like
0Likes
Like

Posted 06 December 2012 - 03:21 PM

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

Sponsor:

#2 fastcall22   Crossbones+   -  Reputation: 2981

Like
1Likes
Like

Posted 06 December 2012 - 03:37 PM

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 );
}


#3 Bregma   Crossbones+   -  Reputation: 4358

Like
0Likes
Like

Posted 06 December 2012 - 04:21 PM

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

#4 mynameisnafe   Members   -  Reputation: 235

Like
0Likes
Like

Posted 07 December 2012 - 06:54 PM

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

#5 mynameisnafe   Members   -  Reputation: 235

Like
0Likes
Like

Posted 08 December 2012 - 09:25 AM

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?

#6 SiCrane   Moderators   -  Reputation: 9124

Like
1Likes
Like

Posted 08 December 2012 - 09:37 AM

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.

#7 e‍dd   Members   -  Reputation: 2097

Like
1Likes
Like

Posted 08 December 2012 - 09:57 AM

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.

#8 mynameisnafe   Members   -  Reputation: 235

Like
0Likes
Like

Posted 09 December 2012 - 03:33 PM

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

#9 e‍dd   Members   -  Reputation: 2097

Like
0Likes
Like

Posted 09 December 2012 - 03:57 PM

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


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


#10 mynameisnafe   Members   -  Reputation: 235

Like
0Likes
Like

Posted 09 December 2012 - 10:13 PM

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




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS