Okay, this might become a monstous post, and I apologize for that. I recently switched from programming Java to C++ like a week ago, and can't seem to get all the pointers right it seems.

Maybe all code provided in this post might seem a little overkill for the size of my problem, but I'm not sure if this problem really is as small as it seems, or if there are other steps along the process that cause this to happen. Maybe you would like to start looking at my outputs first.

The project I'm working on is going to bundle lots of data together into binary packages (both numbers, strings, images and later on sound aswell). I want to do this because I'm making my own game console or so to speak, and I want each of my games inside just one file. Hopefully you get the point.

I've managed to successfully write and read som animation info and some images, but something wasn't right. Sometimes when I compiled and runned the program would crash when upon loading the game data. So I did some research and I think I found the problem. However, first I wish to introduce you to some of my methods to help you understand my problem. They are somewhat rewritten for the purpose of this post, but I tried to preserve the running circumstances of my original program:

To make everything easier, I made a library for some reading and writing:

// Writes a string outStr to BIN file ofs
void stringToBINFile(string *outStr, ofstream *ofs)
{
	int strLen = strlen(outStr->c_str()); //Length has to be known when reading
	ofs->write((char *)&strLen, sizeof(strLen));
	cout << "Wrote string length: " << strLen << endl;
	cout << "Wrote char: ";
	for (int i = 0; i < strLen; ++i)
	{
		ofs->write((char *)&outStr->c_str()[i], sizeof(outStr->c_str()[i]));
		cout << "'" << outStr->c_str()[i] << "' ";
	}
	cout << "\n";
}

// Reads a string inStr from a BIN file ifs
void stringFromBINFile(string *inStr, ifstream *ifs)
{
	int strLen;
	ifs->read((char *)&strLen, sizeof(strLen));
	cout << "Read string length: " << strLen << endl;
	inStr = new string;
	cout << "Read char: ";
	for (int i = 0; i < strLen; ++i)
	{
		ifs->read((char *)&inStr->c_str()[i], sizeof(inStr->c_str()[i]));
		cout << "'" << inStr->c_str()[i] << "' ";
	}
	cout << "\nFinal string: '" << inStr->c_str() << "'. Len: " << strlen(inStr->c_str()) << endl;
}

Then I wrote two test programs, one to compile a BIN file:

void myWrite(char* path, string* outStr)
{
	ofstream ofs (path, ios::binary);
	stringToBINFile(outStr, &ofs);
	ofs.close();
}

int main()
{
	string outString = "WATER";
	myWrite("test1.bin", &outString);
	outString = "SNOW";
	myWrite("test2.bin", &outString);
}

And one to read from the BIN file:

void myRead(char* path, string* inStr)
{
	ifstream ifs (path, ios::binary);
	stringFromBINFile(inStr, &ifs);
	ifs.close();
}

int main()
{
	string inString;
	myRead("test1.bin", &inString);
	cout << inString << endl;
	delete &inString;
	myRead("test2.bin", &inString);
	cout << inString << endl;
}

Output from write program:

Wrote string length: 5
Wrote char: 'W' 'A' 'T' 'E' 'R'
Wrote string length: 4
Wrote char: 'S' 'N' 'O' 'W'

Okay, so far so good. 'test1.bin' exists, and seems to contain the amount of data I wrote to them. However (finally!) I get to the intresting output of my read program:

Read string length: 5
Read char: 'W' 'A' 'T' 'E' 'R'
Final string: 'WATER'. Len: 5
WATER
Read string length: 4
Read char: 'S' 'N' 'O' 'W' 'R'
Final string: 'SNOWR'. Len: 5
SNOWR

EDIT: Corrected order of output

In the final read it seems my string preserves its length from its previous data. For all I know there should be nothing left of the string after I've deleted and allocated a new string to it. Yet it seems to remember its old length and fill out with whatever characters it's missing. Am I not deleting it enough? Or am I simply doing it wrong? I've tried delete [] &inString; but with no success.

Also, this code is not my actual compiled version because I had to cut lots of it down and get rid of parts that are not related to this problem. Therefore you might find errors like a missing * or something somewhere. I ask you to overlook those things (think of it as pseudo code :)) And only point it out if it might actually be what's causing the problem.

Thanks a lot for your time, and it would mean a lot to me if we managed to sort this out!

Emil Olofsson

I haven't read through your whole post, but when deleting strings:

string *inString = new string;
delete inString;

If your string is just a std::string then your write methods are not taking advantage of the methods available to you .c_str() this converts a std::string into a char *

and .size() gets you the length of a string

therefore

std::ofstream fout("test.txt", std::ios::bin);
if(fout.is_open())
{
 fout.write(str.c_str(), str.size());
 fout.close();
}

Couple of general points: In general use references (instead of pointers) for strings, streams and the like. Use const where ever and whenever possible - it is your friend. Prefer using '\n' to std::endl to send a new line to a stream, unless your intent is to also flush the stream (performance).

Now, re. the code to read and write strings using binary i/o:

To get the number of characters in a string use the size() method. As you are dealing with binary stuff, c_str() is a null terminated string; it will mess you up if the string itself contains null characters.

Use the result of c_str() only immediately after calling it; it cannot be safely used once the string is modified.

The modified code would be something like:

// Writes a string outStr to BIN file ofs
void stringToBINFile( const string& outStr, ostream& os )
{
    int n_chars = outStr.size() ; //Length has to be known when reading
    os.write((char *)&n_chars, sizeof(n_chars));
    if( n_chars > 0 ) os.write( (const char *)&*outStr.begin(), n_chars );
}

// Reads a string inStr from a BIN file ifs
void stringFromBINFile( string& inStr, istream& is )
{
    int n_chars;
    is.read((char *)&n_chars, sizeof(n_chars));
    if( n_chars > 0 )
    {
        inStr = string( n_chars, ' ' ) ;
        is.read( (char *)&*inStr.begin(), n_chars ) ;
    }
    else inStr = "" ;
}

void myWrite( const char* path, const string& outStr)
{
    ofstream ofs (path, ios::binary);
    stringToBINFile(outStr, ofs);
}

void myRead( const char* path, string& inStr)
{
    ifstream ifs (path, ios::binary);
    stringFromBINFile(inStr, ifs);
}


int main()
{
    {
        string outString = "WATER";
        myWrite("test1.bin", outString);
        outString = "SNOW";
        myWrite("test2.bin", outString);
    }
    {
        string inString;
        myRead("test1.bin", inString);
        cout << inString << '\n';
        myRead("test2.bin", inString);
        cout << inString << '\n';
    }
}

Thank you for your replies! There surely are a lot of new things to try out there. It might take a while for me to adapt and test all the new code, but I will be back for a status report!

Emil Olofsson

Yeah! Problem solved. Using your suggestions I finally got it to work. Thank you guys!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.