Working with binary

Started by
11 comments, last by smart_idiot 19 years, 2 months ago
I've never done anything involving reading/writing data in binary. However, I find myself with the need to do that very thing. I have a file (.stl) that contains triangle vertex and normal information I am going to read in and build an object from. I know the format of the file, so can I just use the C++ >> operator to grab the info? Or do I need to tell the compiler to convert from binary to something else first? Also, the file has an 80 byte header that contains no pertinant information to the object. I would imagine I can just use a seekg to skip past this section? Thanks in advance.
Without order nothing can exist - without chaos nothing can evolve.
Advertisement
Quote:Original post by CyberSlag5k
so can I just use the C++ >> operator to grab the info? Or do I need to tell the compiler to convert from binary to something else first?


I think the >> operator is only for ASCII data. You have to use the read and write commands from the file stream library.

fin.read(Object, Size);
fout.write(Object, Size);

Quote:Original post by CyberSlag5k
Also, the file has an 80 byte header that contains no pertinant information to the object. I would imagine I can just use a seekg to skip past this section?


You could do that or just use something like this:

char Buffer[80];
ifstream fin;
fin.open(filename);
fin.read(&Buffer,sizeof(Buffer));
<- more reading
fin.close();

EDIT: If you haven't already I suggest you read this article.
Quote:I know the format of the file, so can I just use the C++ >> operator to grab the info?


These are for formatted IO. Binary is unformatted IO. Use the stream's read() and write() member functions. Just make sure you only process POD-types in that way.

Quote:Or do I need to tell the compiler to convert from binary to something else first?


On Microsoft platforms, it is necessary (but not sufficient!) to open the file in binary mode, to disable newline translation.

Quote:Also, the file has an 80 byte header that contains no pertinant information to the object. I would imagine I can just use a seekg to skip past this section?


Yes. Though you should rely on the header to make sure you have a file in the correct format. Headers are rarely completely useless.
"Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it." — Brian W. Kernighan
I've played around with it a bit, and it seems the read function only accepts a character as a parameter. I initially tried this:

float temp;
fin.read(temp, 4);

but that didn't compile. Should I be reading in the entire set of data as a character buffer and then convert pieces of that buffer to floats? Seems like kind of a pain. Is there a better way? And aren't I going to lose some precision doing it that way? Or does specifying that 4 bytes should be read in prevent the loss of data?

[Edited by - CyberSlag5k on February 7, 2005 11:31:20 AM]
Without order nothing can exist - without chaos nothing can evolve.
iostreams are for formatted i/o only. Don't use them for binary operations. If you want to do that sort of thing, grab their stream buffer and manipulate that directly.
Chess is played by three people. Two people play the game; the third provides moral support for the pawns. The object of the game is to kill your opponent by flinging captured pieces at his head. Since the only piece that can be killed is a pawn, the two armies agree to meet in a pawn-infested area (or even a pawn shop) and kill as many pawns as possible in the crossfire. If the game goes on for an hour, one player may legally attempt to gouge out the other player's eyes with his King.
A typical istream object's read() function expects a pointer to a character buffer. The quickest way to read a multi-byte variable (such as a 4-byte float) is to cast the address of that float as a char*, and tell read() to read 4 bytes into that character buffer.
float temp;fin.read((char*)&temp, sizeof(temp));
This may have portability issues (little/big-endian), but what lazy programmer cares about those? [wink] I never know, 'cause I never work with anything other than Windows.
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Hmm...I'm getting some weird results. Here's roughly what I'm doing:

void loadListBinary(){LinkedList vertexList;LinkedList normalList;Vector normVector;Vector tempVector;char header[80];char buffer[50];int numFacets;ifstream fin;fin.open(fileName, ios::binary);if(fin.is_open() == false)exit(333);fin.read(header, 80);fin.read(buffer, 4);numFacets = int(buffer[0]);for(int i = 0; i < numFacets; i++){fin.read(buffer, 50);normVector.setVector(buffer[0], buffer[4], buffer[8]);normalList.InsertItem(normVector);fout << normVector << endl;tempVector.setVector(buffer[12], buffer[16], buffer[20]);vertexList.InsertItem(tempVector);fout << tempVector << endl;tempVector.setVector(buffer[24], buffer[28], buffer[32]);vertexList.InsertItem(tempVector);fout << tempVector << endl;	tempVector.setVector(buffer[36], buffer[40], buffer[44]);vertexList.InsertItem(tempVector);	fout << tempVector << endl << endl;}}


The format of the file is:

Any text such as the creator's name (80 bytes)int equal to the number of facets in file (4 bytes)//facet 1 float normal x (4 bytes)float normal yfloat normal zfloat vertex1 x (4 bytes)float vertex1 yfloat vertex1 zfloat vertex2 xfloat vertex2 yfloat vertex2 zfloat vertex3 xfloat vertex3 yfloat vertex3 zunused (padding to make 50-bytes) //facet 2float normal xfloat normal yfloat normal zfloat vertex1 xfloat vertex1 yfloat vertex1 zfloat vertex2 xfloat vertex2 yfloat vertex2 zfloat vertex3 xfloat vertex3 yfloat vertex3 zunused (padding to make 50-bytes) //facet 3  ... 


Unfortunately, since the file is in binary it is difficult for me to see what values should be read in, but when I output the contents of my linked lists the first dozen or so vertices are all 0's and the rest values not what I think they should be. This makes me think I'm doing something quite wrong. Any help would be appreciated.
Without order nothing can exist - without chaos nothing can evolve.
Change char buffer[50]; to int buffer[13]; and divide all indices by 4 then it should work.

Quote:Original post by Trap
Change char buffer[50]; to int buffer[13]; and divide all indices by 4 then it should work.
No, then it'll interpret binary float values as int values, and things will still get screwed up.

There are a variety of ways to get this to work. One is just reading it straight into a float value as I specified above. Another, working with the code just posted, is to change the calls to setVector() to look like this:
normVector.setVector(*((float*)(buffer + 0)), *((float*)(buffer + 4)), *((float*)(buffer + 8)));

Somehow or another, you need to make the compiler treat some data as another type of data temporarily. This is easiest to accomplish using pointer casts. If you just cast normal data (such a buffer[0] (a char) to float), it'll just convert the first 8 bits to a float number, rather than interpretting 32 bits as though they represented a float. So you need to get the address of buffer[n] (which could be "&buffer[n]", or "buffer + n") and then treat that address as though it were an address to a float, not to a char. Or if you're reading straight into a float, as in my previous post, you need to make the address to the float act like an address to a char buffer. Make sense?
"We should have a great fewer disputes in the world if words were taken for what they are, the signs of our ideas only, and not for things themselves." - John Locke
Quote:Original post by Agony
Quote:Original post by Trap
Change char buffer[50]; to int buffer[13]; and divide all indices by 4 then it should work.
No, then it'll interpret binary float values as int values, and things will still get screwed up.

There are a variety of ways to get this to work. One is just reading it straight into a float value as I specified above. Another, working with the code just posted, is to change the calls to setVector() to look like this:
normVector.setVector(*((float*)(buffer + 0)), *((float*)(buffer + 4)), *((float*)(buffer + 8)));

Somehow or another, you need to make the compiler treat some data as another type of data temporarily. This is easiest to accomplish using pointer casts. If you just cast normal data (such a buffer[0] (a char) to float), it'll just convert the first 8 bits to a float number, rather than interpretting 32 bits as though they represented a float. So you need to get the address of buffer[n] (which could be "&buffer[n]", or "buffer + n") and then treat that address as though it were an address to a float, not to a char. Or if you're reading straight into a float, as in my previous post, you need to make the address to the float act like an address to a char buffer. Make sense?


Right, my concerns were that I would be attempting to store the data of a float in the sizoe of a character and I would lose part of the data. I tried modifying my original code based on your first post, but the shape drawn is garbage. Here's what I have now:

void loadListBinary(){	Vector normVector;	Vector tempVector;	char float1, float2, float3;	char header[80];	char buffer[50];	int numFacets;	ifstream fin;	fin.open(fileName, ios::binary);	if(fin.is_open() == false)		exit(333);	fin.read(header, sizeof(header));	fin.read((char*)&numFacets, sizeof(numFacets));	for(int i = 0; i < numFacets; i++)	{		fin.read((char*)&float1, sizeof(float1));		fin.read((char*)&float2, sizeof(float2));		fin.read((char*)&float3, sizeof(float3));		normVector.setVector(float1, float2, float3);		normalList.InsertItem(normVector);		fin.read((char*)&float1, sizeof(float1));		fin.read((char*)&float2, sizeof(float2));		fin.read((char*)&float3, sizeof(float3));		tempVector.setVector(float1, float2, float3);		vertexList.InsertItem(tempVector);		fin.read((char*)&float1, sizeof(float1));		fin.read((char*)&float2, sizeof(float2));		fin.read((char*)&float3, sizeof(float3));		tempVector.setVector(float1, float2, float3);		vertexList.InsertItem(tempVector);		fin.read((char*)&float1, sizeof(float1));		fin.read((char*)&float2, sizeof(float2));		fin.read((char*)&float3, sizeof(float3));		tempVector.setVector(float1, float2, float3);		vertexList.InsertItem(tempVector);	}	vertexList.ResetList();	normalList.ResetList();	fin.close();}


The shape just comes up garbage. Am I doing somewthing wrong?
Without order nothing can exist - without chaos nothing can evolve.

This topic is closed to new replies.

Advertisement