Reading Binary Files
I'm trying to develop a small, portable, custom binary file to store things for my game. I found a site that shows how to write out structs to a binary file quite easily. It also showed an easy way to read this structure. Here's what it shows:
Is there a way to write out a struct like this but then read in the individual parts? For instance I save the example struct but I want to recall the letter and assign it to one thing but the number to another. I don't want to just read in to an identical struct. How would I go about this.
http://www.gamedev.net/reference/article...le1127.asp Wrote:struct OBJECT
{
int number;
char letter;
} obj;
obj.number = 15;
obj.letter = ëM�*;
fout.write((char *)(&obj), sizeof(obj));
That would write the entire structure for you! Let�*s move on to input now. Input will be a cinch now because the read() function takes exactly the same parameters as write(), and it works exactly the same.
ifstream fin("file.dat", ios::binary);
fin.read((char *)(&obj), sizeof(obj));
Is there a way to write out a struct like this but then read in the individual parts? For instance I save the example struct but I want to recall the letter and assign it to one thing but the number to another. I don't want to just read in to an identical struct. How would I go about this.
You probably should allocate a temporary instance of your structure and read it, grab the values from that, and then copy them to where you want them to go. If you try to read the numbers in separately you have to deal with not only the byte order but also you have to guess at how the structure is padded and aligned. And by portable I hope you don't mean that you can copy the data files across computers, because for the same reasons you probably can't using this method. YMMV
Did you ever wonder why we had to run for shelter when the promise of a brave new world unfurled beneath the clear blue sky?
Why wouldn't I be able to use the file on multiple computers? Even if I load with the temporary struct, it still wouldn't work? Why not?
@Nick: I'd presume that by "multiple computers" Steven is really talking about multiple architectures/OS (your usual "fun with endianess" issues...)
The code would also likely break on even only the one OS if, at an indeterminate point in the future, you switched compilers* with the new compiler re-ordering or padding the members of the struct to perform it's own, different, optimisations...
This is the reason why you can't just read an individual member from the file – as you don't know what changes the compiler has made to the struct, you don't know where the member you're looking for might be in the file – you're forced to "fin.read((char *)(&obj), sizeof(obj));" the entire object, and then retrieve objects as "char myChar = obj.letter;"
(*although I suppose not that likely on the Mac unless you're already using CodeWarrior.)
The code would also likely break on even only the one OS if, at an indeterminate point in the future, you switched compilers* with the new compiler re-ordering or padding the members of the struct to perform it's own, different, optimisations...
This is the reason why you can't just read an individual member from the file – as you don't know what changes the compiler has made to the struct, you don't know where the member you're looking for might be in the file – you're forced to "fin.read((char *)(&obj), sizeof(obj));" the entire object, and then retrieve objects as "char myChar = obj.letter;"
(*although I suppose not that likely on the Mac unless you're already using CodeWarrior.)
Mark Bishop
So is it a bad way to go to save my files this way? I want the most cross-platform way to save files. I'm going to save my maps and all sorts of things. I'm using just SDL and C/C++ so there are no Cocoa classes to help me. I'm just sorting out some of the "finer" details of programming.
It's not a bad way to save files, unless you want it to be cross platform. Now if you are designing a class that does all the work for you, you could write functions that would take into account the endianness of the system you are on and properly write/read the file.
It's definetly a fast and easy way to save files, and I prefer it to writing out long text files of information. Steven's advice is a little misleading, you can copy them across computers, just not ones with different architectures or OS's.
It's definetly a fast and easy way to save files, and I prefer it to writing out long text files of information. Steven's advice is a little misleading, you can copy them across computers, just not ones with different architectures or OS's.
Hm. Interesting. And here I thought using straight C/C++ would be perfect. Any tips on where to look as to writing a class that does all the work for me? Seems slightly daunting.
"different architectures" being "the mac" now that we're running on both PowerPC and Intel. Never, ever, ever, do what the code in the OP does. Not only is it guaranteed to fail on whichever of PowerPC and Intel didn't write the original file, but it's likely to fail in a move to a different ABI when the alignment of various types changes.
You should always read a specific number of bytes into a buffer of memory, then manually extract the bytes you want, swap them as appropriate, then put them wherever you want them.
If you want a marginally easier way of doing that than lots of pointer math, you could try http://onesadcookie.com/svn/repos/libBinaryIO/ -- a printf/scanf-like API for reading and writing binary data.
You should always read a specific number of bytes into a buffer of memory, then manually extract the bytes you want, swap them as appropriate, then put them wherever you want them.
If you want a marginally easier way of doing that than lots of pointer math, you could try http://onesadcookie.com/svn/repos/libBinaryIO/ -- a printf/scanf-like API for reading and writing binary data.
Back in the good old pre-Carbon days we used to GetHandle() and BlockMove()
I miss those days
I miss those days
"When you dream, there are no rules..."
What you need to do is design your own file format. Then you allocate a buffer with malloc or new[] and fill that out. That way you will sidestep all problems with padding, since there is none. (You designed it not to have any) Then, if you have multibyte data in it, you need to detect if you are on a big- or little-endian system. If you're on a system different than the one that made the file, you have to swap the bytes to match. Google for endianness or byteswapping.
As for the first part, it's easy.
Write:
Reading:
I'm sure you get the idea
As for the first part, it's easy.
Write:
Code:
char *buffer = (char *) malloc (100); // Or whatever size you need
char *bufPtr = buffer; // A pointer to the first byte
memcpy (bufPtr, thePlayer->numOfLives, sizeof (int));
bufPtr += sizeof (int);
memcpy (bufPtr, thePlayer->money, sizeof (int));
bufPtr += sizeof (int);
fwrite (as usual, just dump the buffer)Reading:
Code:
char *buffer = (char *) malloc (100); // Or whatever size you need
char *bufPtr = buffer; // A pointer to the first byte
fread (as usual, just read the entire file)
memcpy (thePlayer->numOfLives, bufPtr, sizeof (int));
bufPtr += sizeof (int);
memcpy (thePlayer->mone, bufPtr, sizeof (int));
bufPtr += sizyeof (int);
Would something like this work? I get a bus error when I run it in the terminal. I'm probably doing the fwrite wrong. I haven't used that function much.
Code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct TestClass
{
int age;
int lives;
};
int main()
{
TestClass myObj;
myObj.age = 24;
myObj.lives = 4;
char *buffer = (char *)malloc(100);
char *bufPtr = buffer;
memcpy(bufPtr, (int *)myObj.age, sizeof(int));
bufPtr += sizeof(int);
memcpy(bufPtr, (int *)myObj.lives, sizeof(int));
bufPtr += sizeof(int);
FILE *testFile;
testFile = fopen("test.dat","wb");
fwrite(bufPtr,sizeof(int),2,testFile);
fclose(testFile);
return 0;
}
I'd first put in some error detection to check whether the file you're trying to fwrite() to has actually been created; I'd then wonder long and hard about why you're creating a char* variable and using memcpy() to store ints in it... I haven't read through all the source, so this may be a silly question... but why?
Oh, and I haven't played with pointer arithmetic for quite a while, but from my memory you're passing 'bufPtr' to fwrite() without first reseting it's address; that will probably cause a few errors (but reading the code, you won't even get that far...), and you probably want to pass the original 'buffer' instead.
Try something like this...
Oh, and I haven't played with pointer arithmetic for quite a while, but from my memory you're passing 'bufPtr' to fwrite() without first reseting it's address; that will probably cause a few errors (but reading the code, you won't even get that far...), and you probably want to pass the original 'buffer' instead.
Try something like this...
Code:
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#ifdef __cplusplus
};
#endif
class TestClass
{
public:
int m_lives;
void Save( void )
{
FILE *file;
if(( file = fopen( "sealfin.bin", "wb" )) == NULL )
{
printf( "Scream! And die!" );
return;
}
fwrite( &m_lives, sizeof( int ), 1, file );
/* Yes, I know I should probably check that the int has actually been written to the file, but I'm in a rush... */
fclose( file );
return;
};
void Load( void )
{
FILE *file;
if(( file = fopen( "sealfin.bin", "rb" )) == NULL )
{
printf( "Scream! And die!" );
return;
}
fread( &m_lives, sizeof( int ), 1, file );
/* ...and here I should be checking that an int has actually been read from the file. */
fclose( file );
return;
};
};
int main()
{
TestClass obj;
obj.Load();
printf( "\nlives: %d\n\n", obj.m_lives );
obj.m_lives = 21;
obj.Save();
return 0;
}Mark Bishop
Uh, I just took a second look; your bus error was caused by this:
Which should have been this:
You were dereferencing the variable which you should have been passing the address of (i.e., you were passing it's value as the address); my points on the pointer arithmetic, and the error checking, still stand though
Code:
memcpy(bufPtr, (int *)myObj.age, sizeof(int));Code:
memcpy(bufPtr, &myObj.age, sizeof(int));You were dereferencing the variable which you should have been passing the address of (i.e., you were passing it's value as the address); my points on the pointer arithmetic, and the error checking, still stand though
Mark Bishop
I knew about error checking and such. Normally I do that but I was just trying to figure out writing the files. Thanks for the tips and the fix.
The char thing was just me being absent minded and just waking up, trying to make sense of things I hadn't ever really worked with.
The char thing was just me being absent minded and just waking up, trying to make sense of things I hadn't ever really worked with.
Using your technique, sealfin, how would I save multiple variables and recall them in the proper order?
Possibly Related Threads...
| Thread: | Author | Replies: | Views: | Last Post | |
| Binary Tree and Objective-c | mnorton | 0 | 2,906 |
Sep 7, 2009 10:44 AM Last Post: mnorton |
|
| Carbon - Reading / Writing Text Files | dave05 | 10 | 4,007 |
Aug 1, 2006 05:42 PM Last Post: dave05 |
|
| Reading/writing files in Carbon? | ia3n_g | 2 | 2,312 |
Jul 23, 2006 06:54 PM Last Post: ia3n_g |
|

