Writing to a .dat file using ofstream

Started by
4 comments, last by nobodynews 14 years, 5 months ago
Hi, I'm trying to add some code to my application that saves a highscore out to a text file. I've already written the code that loads the high score in without a problem, which looks like this...

        //  Create a file pointer for opening and reading files
	std::ifstream isGetData;

	//  Open the .dat file that contains the data
	isGetData.open( "HighScore.dat" );

	//  Check to ensure file was found
	if( isGetData.fail() )
	{
		MessageBoxA( m_hWnd, "Failed to find data file HighScore.dat", "Error!", MB_OK | MB_ICONEXCLAMATION );
	}

	//  Return pointer to the beginning of the file and clear the end flag
	isGetData.seekg( NULL, std::ios::beg );
	isGetData.clear();

	//  Create a temporary pointer for reading in data
	std::string strLine = "";
	std::string strTemp = "";

	unsigned short int usiHighScore = 0;
	std::string strHighScore = "";

	while( getline( isGetData, strLine, '\n' ) )
	{
		if ( strLine == "<HighScoreNumber>" )
		{
			isGetData >> usiHighScore;
		}
		if ( strLine == "<HighScoreText>" )
		{
			isGetData >> strHighScore;
		}
	}

	//	Create a new stringstream to hold the new score without the colon
	std::stringstream ssNewScore;
	ssNewScore << m_iFirstDigit << m_iSecondDigit << m_iThirdDigit << m_iForthDigit;

	
	// Convert the new score to an unsigned short int
	unsigned short int usiNewScore = atoi( ssNewScore.str().c_str() );

	m_pText->DrawFont( 150, 100, 300, 300, "HIGHEST SCORE: " + strHighScore ); 
	m_pText->DrawFont( 150, 220, 300, 300, "NEW TIME: " + m_ssClock.str()  );
	

	//  Compare the new score to the current high score to see if it has been beaten
	if( usiNewScore > usiHighScore ) 
	{
		m_pText->DrawFont( 150, 330, 300, 300, "Well Done You Have Set" );
		m_pText->DrawFont( 150, 370, 300, 300, "A New High Score!" );
	}
	else
	{
		m_pText->DrawFont( 150, 330, 300, 300, "Unlucky Better Luck Next" );
		m_pText->DrawFont( 150, 370, 300, 300, "Time!" );
	}
What I need to do now is either replace the high score value stored in HighScore.dat or overwrite it completely. The .dat file has the following format...

<HighScoreNumber>
0055

<HighScoreText>
00:55

I have started by creating an ofstream in the following manner: std::ofstream ofSaveData("HighScore.dat", std::ios::binary); I then tried using ofSaveData.write(); to write stuff to the text file, however I passed the function a stringstream using .str().c_str() but couldn't figure out how to get text on to different lines. Also when I passed the string stream into the .write function I used sizeof(theStringStream) which added a lot of random characters to the end. Any help would be much appreciated cheers!
Advertisement
Why are you writing it in binary? Why wont simple ascii due?


//SAVINGstd::ofstream file(SOME_PATH);if(file){  file << DATA << "\n";  file << DATA << "\n";  file << DATA << "\n";  file << DATA << "\n";  file.close();}//LOADINGchar buff[SOME_SIZE];std::ifstream index(SOME_PATH);if(file){  while(index.getline(buff, sizeof(buff)))  {      //get data from buff  }  file.close();}
@Crazyfool

If you are going to write variable length data to a text file, you might as well read it in kind:
std::string line;std::ifstream file("somefile.txt");while(std::getline(file, line)){   // use line...}// std::fstream objects close themselves when they go out of scope.


@Slyfox
Do you want the file format to be text or binary? The ".dat" suffix usually hints that the file contains data which you need to use its format to understand. But your suggested format is a text based one, with no implicit format.

Writing the data out is quite easy:
namespace{    // Define these somewhere external so if they need to change it    // only has to happen once.    const char *highScoreNumberTag = "<HighScoreNumber>";    const char *highScoreTextTag = "<HighScoreText>";}void writeScore(int score, Time time){    std::ofstream out("HighScore.dat");    out << highScoreNumberTag << '\n' << score << '\n';    out << highScoreTextTag << '\n' << time.minutes << ':' << time.seconds << '\n';}


When you use write(), you need to be very careful because you are dealing with pointers and byte counts. So, sizeof(std::stringstream) is a constant number - the number of static bytes (determined at compile time) of a std::stringstream instance.

If you want to get the length of its string, you would probably want to capture the returned string from it's str() method:
void write(std::stringstream stream, std::ostream &binaryOut){    std::string string = stream.str();    binaryOut.write(string.c_str(), string.size());}
Rip-off has the right idea.

Problem here is you're mixing write types all over the place basically. Simplest way to do what you're trying to do is just output the score in a .txt file and use headers like you wrote using the normal ASCII output.

It essencially isn't going to work right now because the file is in binary but you're trying to read it in like a .txt document. Binary doesn't work the same way as ASCII outputting does, ASCII has whitelines, spacing, and uses different methods. Binary just writes all the data together in binary form, you know, 1's and 0's. When you write in binary it more or less just makes a really long line of data and you can only read it in if you know the format of how you wrote the information(byte size and such), otherwise if you try and open the file in anything else it comes out as gibberish.

I'd stick to simplicty and use a .txt file but either way you need to change it up.
Cheers guys, using a combination of you're help I have managed to get an ascii version working.

However because it is supposed to be a highscore document I would prefer it if it were in a form such as binary so that it won't be so easy to crack. At the moment anyone can just open up the file and edit their highscore. How would you go about implementing a binary form of what I'm trying to do?
Quote:Original post by Slyfox
At the moment anyone can just open up the file and edit their highscore.
So? If some guy wants to 'cheat' why do you care? Only during online stuff does it matter what the score is and in that case you shouldn't trust anything the user does anyway.

That said, if you want to make it slightly harder create a checksum. Nothing fancy, just add up all of the high scores and then add, say, 1234567 to that number and write that to end of the high score list. Like this (for 5 high scores):
ABC 51000ZED 47878BOB 33333JOE 22222YES 01389000
This at least requires the user to reverse engineer the checksum. That wouldn't be too hard to circumvent, but then neither would be storing the scores in binary.

C++: A Dialog | C++0x Features: Part1 (lambdas, auto, static_assert) , Part 2 (rvalue references) , Part 3 (decltype) | Write Games | Fix Your Timestep!

This topic is closed to new replies.

Advertisement