Skip to main content
gamedev.net
🔒 Locked

storing std::string on disk

Started by xSKOTTIEx 11 replies 2k views
Original Post
xSKOTTIEx
xSKOTTIEx
i want to store game information on disk, in a binary file. unfortunately, i don't know the internal structure of an std::string. will storing it on file be a problem?, considering the length of a string can vary, etc? I know if i give it a buffer that is concievebly big enough, it wont overrun the buffer, but will the information be stored/retrieved correctly? for file input/output im using fstreams. thanks for your help guys!
---------Coming soon! Microcosm II.
cmptrgear
cmptrgear
Why cant you just do this. Check for errors and correct opening of files of course.

std::string foo = "foobar"std::string bar;std::ofstream output("mysavefile.txt");output << foo;std::ifstream input("mysavefile.txt");input >> bar;
"Pfft, Facts! Facts can be used to prove anything!" -- Homer J. Simpson
rpg_code_master
rpg_code_master
std::string is dynamic enough to re-size the string it contains. This is useful as you can read in an n-sized string from a file with minimal problems.
rip-off
rip-off
you can store the raw character array if you can guarantee that your string has no nul character is it('\0').

If it does, you can just write an int out first, its size, followed by the character array...
xSKOTTIEx
xSKOTTIEx
the most important piece to this is that it is a BINARY file.

in that, it is nothing but an array of bytes.

seekg
readg

are the methods that i am using here. in that, if i pass it a buffer of 100 bytes, just to be safe (assuming the the name is very small), will the contents of the string be preserved, because a dynamic char array (char*) is only four bytes. but the name itself could actually be 20 bytes... etc. see what im saying?
---------Coming soon! Microcosm II.
hplus0603
hplus0603
The general problem you're trying to solve is called "serialization". You want to serialize your game state to a stream (disk) such that you can later de-serialize (inflate) that stored state to new objects.

Using operator<<() is one way of serializing, although it's not guaranteed to support de-serialization (there are no string delimiters output to a standard text output, for example).
enum Bool { True, False, FileNotFound };
Fruny
Fruny
Quote:
Original post by xSKOTTIEx
see what im saying?


Don't try to save the object itself as a binary blob, it won't work. People who try to blit (as a byte array) non-trivial types (e.g. non-POD) all over the place should be shot. You need to save each data member individually, possibly saving its own data members individually, etc.

You must take care of saving the data it contains instead if necessary). Whether you save it as a fixed-size field in the file, or whether you want to deal with the variable string length is entirely up to you. In both cases, you will have to deal with overflow prevention:

* fixed-width: writes can overflow the field in the file.
* variable-width: reads can overflow the preallocated buffer in the string object.

Decide one way or the other and take the time to do it right or use a serialization librar, like the one found in Boost.
"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
_Sigma
_Sigma
I actually wrote a little class to handle reading and writing strings, etc, to a file in both binary and text modes. Take a lookie if you want. Its pretty straight forward to use. It is all templated functions, so its reasonably diverse.
Promit
Promit
A couple things of note. std::string is not null terminated. It is not even vaguely safe to write the std::string directly to file (meaning fwrite or ostream::write). Using the insertion operator with an ostream won't tell you how long the string is. The best way to do this is to write the string to disk (using string::data() rather than string::c_str()), preceded by a 7 bit encoded length. Then when you're reading the string back, you can read into a temporary buffer and then construct a string from it.

This of course assumes that you only ever use ASCII strings.
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
Tonic151
Tonic151
Quote:
Original post by Promit
A couple things of note. std::string is not null terminated. It is not even vaguely safe to write the std::string directly to file (meaning fwrite or ostream::write). Using the insertion operator with an ostream won't tell you how long the string is. The best way to do this is to write the string to disk (using string::data() rather than string::c_str()), preceded by a 7 bit encoded length. Then when you're reading the string back, you can read into a temporary buffer and then construct a string from it.

This of course assumes that you only ever use ASCII strings.


I've never had problems with just writting in ofstream << string << endl;

just put a /n at at the end of each line, then you can read each one easily with getline().


But overall if you want to do some stuff where your want to read and write dynamic text of different sizes, use a (2) flat files system, the first file will contain and index, offset, and possibly the text size. While the second files contain's only the text.

bascially you would read the first index file, get the offset and the length, then read the text in from the second file, that way the first file can be binary and read/write easily with structures that will grab that dynamic text offset's of the other file. Alot of message systems / forums work this way.
Sharlin
Sharlin
Quote:
just put a /n at at the end of each line, then you can read each one easily with getline().


Ah, but what if the string itself may contain newlines?
jkleinecke
jkleinecke
Someone hinted at a good solution earlier. Don't worry about saving the whole object, just save the text. For a binary file, first save out a short (2 bytes) for the size of the string, and then save out the char array using '.c_str()'. To load it back in, first read the size of the text in, and then create a character array that can hold the text. Next, load the data in the char array you just created. Now all you have to do is assign the char array to a std::string and you have all your data back.
-----------------------------------Indium Studios, Inc.
Xai
Xai
do what everyone writing any dynamically sized memory all through time has done (I know, I'm exagerating here) ... write out the size of the string first, then the bytes of the string ... then read back the size, then the bytes ... that's what you have to do with any type which has no sentinel value (for instance a bitmap).

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.