Sign in to follow this  
Ignifex

C++ Saving class with char* to a file

Recommended Posts

Hi, I'm trying to make a simple save/load system using fstream. I basically want to save a class to a file and then be able to load it again later. All the data I want to save, is all stored in one class. I decided to start simple and ran into a problem there. I am normally able to save and load int, double and char types, but I can't seem to find a way to save a char*/string (or vector, but I think it's the same problem here). With a char* the size changes as you give it a value (right?). For some reason when I do like: sizeof(my-char*-var); I always get 4. But it can't be 4, right? So if the size is not known, I can't save it. I've been searching all over, but I can't find out how to do this. How do I get the size of a char*? Or am I doing something very wrong by even thinking of the size of char*? It is a pointer after all. But then how can I save a class with a string to a file? Help is very welcome. Thanks in advance. Ignifex

Share this post


Link to post
Share on other sites
Yes, you are incorrect in using
sizeof(char *)
What you're going to have to do is use
sizeof(char)*(strlen(mystring)+1)
The '+1' is to include the null-terminator.

Hope that helps.

Also, it's been a while since I've done this, someone please correct me if I've made a mistake.

Share this post


Link to post
Share on other sites
Well, that does return the size in characters yes. :)
But will it help me save it to a file? I actually need the bytesize it uses.
Now this is probably because my post was focused on the size of the char* too much, but what I was actually wondering was:
How do I get the bytesize of my whole object? I think I am now beginning to understand something about the char*'s size. It's always 4, because it's a pointer and that stays the same. But then how do I make it so that everything is saved along in the file, so also the data behind the char*?
Just to make it a lot planer:
How do I save my entire class to a file, so I can load it up entirely later?
Thanks :)

Share this post


Link to post
Share on other sites
You need to make your load and save functions save each variable individually (Actually, if you have 3 ints after one another, you can write all 3 as a chunk of 3*sizeof(int) bytes).
If you have this structure:
struct foo {int n, char* str}; and str is the string "Hello World" (11 chars + NULL terminator), then you need to write sizeof(int) bytes from &n, and then you need to write strlen(str) bytes from str. You can add 1 to that if you also want to write the NULL, but its not really needed.

Theres no way to just write everything from a class in one go, unless you want to write the value of the pointers, and not the data at them.

Share this post


Link to post
Share on other sites
Ok, thanks.
I'll just have to look into the reading part some more then and save all variables individually (really can't wait :/) It's quite a bit of data, since I was planning on putting the actual gamedata in seperate files too, to make the gamebuilding a lot faster.
And yes, you're right. 1 char is 1 byte (forgot for a second... *cough*)

Share this post


Link to post
Share on other sites
You had just replied while I was typing my reply, so I was actually only replying to pinguindude there, sorry :)
Yes, your post does return the byte amount, but since sizeof(char) = 1, you could just as well leave that away.

Share this post


Link to post
Share on other sites
Not a problem.

As for sizeof(char)==1, this is true, but to be honest I'm used to writing portable software for different systems, and so it's generally a good idea to put the sizeof in there just to be safe for different compilers and operating systems.

Share this post


Link to post
Share on other sites
Assuming sizeof(char) == 1 might be considered sloppy coding. If you try to take your code into a Unicode or MultiByte Character Set environment you will get burned. Always design and implement your code to handle the (fairly likely) possibility that someday the sizes of data types might change. Also, explicitly checking data type sizes can make it much simpler to convert your code to using other data types later - and if you get into templating in C++, making assumptions about the data types can be fatal.

Share this post


Link to post
Share on other sites
At least in C++, sizeof(char) is guaranteed to be 1 by the standard by definition.

The "sloppy coding", again at least in C++, comes from assuming that a variable of type "char" independantly represents a character of text.

For the love of all that is holy, PLEASE use std::string in C++.

As to the OP's question: Hopefully this answers your question.

Share this post


Link to post
Share on other sites
I am just trying to get something very simple to work for me now. So I made two small projects, called writer and reader, which write and read from a file. I can save and load int and I think I can save char* too, but I don't get how to load char*'s. When I have saved the char* to a file (with it's size as an int in front of it), I can see the text in the binairy file, so I guess that's ok.
The char*, when reading, just appears blank.
I've got this:
//header file
struct myclass {
int int1;
int int2;
char* mychar;
};

//writer file
void writefile() {
somedata.int1 = 5573;
somedata.int2 = 21957;
somedata.mychar = "testing";

fstream file("./test.dat",ios::out|ios::binary);
file.write(reinterpret_cast<char *>(&somedata.int1),sizeof(int));
file.write(reinterpret_cast<char *>(&somedata.int2),sizeof(int));
int csize = (strlen(somedata.mychar)+1)*sizeof(char);
file.write(reinterpret_cast<char *>(&csize),sizeof(csize));
file.write(reinterpret_cast<char *>(&*somedata.mychar),csize);
file.close();

MessageBox(NULL,"Done writing :)","Done",MB_OK|MB_ICONEXCLAMATION);
};

//reader file
void readfile() {
long size;
fstream file("./test.dat",ios::in|ios::binary);
file.seekg(0, ios::end );
size = file.tellg();
file.seekg (0, ios::beg);
file.read (reinterpret_cast<char *>(&somedata.int1), sizeof(int));
file.read (reinterpret_cast<char *>(&somedata.int2), sizeof(int));
int csize;
file.read (reinterpret_cast<char *>(&csize), sizeof(int));
file.read (reinterpret_cast<char *>(&*somedata.mychar), csize);
file.close();

MessageBox(NULL,somedata.mychar,"Result",MB_OK|MB_ICONEXCLAMATION);
};

Share this post


Link to post
Share on other sites
Quote:
Original post by Ignifex
when reading, just appears blank.


well i'm not surprised with a line like this:


file.read (reinterpret_cast<char *>(&csize), sizeof(int));
file.read (reinterpret_cast<char *>(&*somedata.mychar), csize);


first thing you should know is that string literals are basically implicitly static & constant so modifiying a string literal through a pointer causes undefined behaviour.

The solution is to dynamically allocate memory and give the address of it to read e.g.


file.read(reinterpret_cast<char *>(&csize), sizeof(int));

somedata.mychar = new char[csize];

file.read(somedata.mychar, csize);

//....
delete [] somedata.mychar;


Also i noticed your writing out the null-terminator, you already include the length of string before it you don't need to write that aswell.

here is a little hint: your using c++ so use c++ strings that is std::basic_string & its type alias std::string.

Share this post


Link to post
Share on other sites
Are you trying to make things difficult for yourself? In C++ the type you use to represent text is std::string. Streams are designed so you can write the code to serialise/deserialise objects in functions and reuse them naturally.

#include <fstream>
#include <iostream>
#include <string>

//header file
struct myclass {
int int1;
int int2;
std::string mychar;
};

std::ostream& operator<<(std::ostream& stream, const myclass& object)
{
stream.write(reinterpret_cast<const char*>(&object.int1), sizeof(object.int1));
stream.write(reinterpret_cast<const char*>(&object.int2), sizeof(object.int2));
stream << object.mychar << '\0';
return stream;
}

std::istream& operator>>(std::istream& stream, myclass& object)
{
stream.read(reinterpret_cast<char*>(&object.int1), sizeof(object.int1));
stream.read(reinterpret_cast<char*>(&object.int2), sizeof(object.int2));
std::getline(stream, object.mychar, '\0');
return stream;
}

myclass somedata;

//writer file
void writefile() {
somedata.int1 = 5573;
somedata.int2 = 21957;
somedata.mychar = "testing";

std::fstream file("./test.dat", std::ios::out | std::ios::binary);
file << somedata;
file.close();
std::cout << "Done writing\n";
};

//reader file
void readfile() {
std::fstream file("./test.dat", std::ios::in | std::ios::binary);
file >> somedata;
file.close();
std::cout << "Done reading\n";
};

int main()
{
writefile();
somedata.int1 = 0;
somedata.int2 = 0;
somedata.mychar = "";
readfile();
std::cout << somedata.int1 << ", " << somedata.int2 << ", " << somedata.mychar << '\n';
}


Enigma

Share this post


Link to post
Share on other sites
Ok, thank you very much!
This just proves I'm still a complete noob at C++ and I hardly know any functions. At least I can now write strings (as std::string ;)) and read it normally.
I'll just play around with this whole some more to get vectors to work (shouldn't be a problem).
Thanks again.

Share this post


Link to post
Share on other sites
Heres a horrible horrible hack as well: If the strings you're storing are static - as in they're all defined in the code somewhere, and not read from a file or input by the user, then you can write the pointer value of the string, and read it in. Because static strings are stored in the exe, and this *should* be in the same place when you load the exe next time.
However, as I said, this is horribly hacky, relies on the exe being loaded at the same place every time, relies on the strings being pointers to static strings in the exe, and is really, really horrible.

I don't know why I bothered to say it actually :P

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this