Writing to a file, need help

well, I'm trying to learn how to write binary files, and I seem to have hit a snag.


1
2
3
4
5
6
7
8
void save()
{
	fstream myFile ("data.bin", ios::in | ios::out | ios::binary);
    myFile.seekp (0);

	myFile.write ((char*)&myLeader, sizeof (myLeader));
	myFile.close();
}


I've been looking at
http://courses.cs.vt.edu/~cs2604/fall02/binio.html

for help.

The problem seems to be that it's not creating a data.bin file. It isn't giving me any errors though.

Advice?
Check if myFile is good before using it...

1
2
3
4
5
6
7
8
9
10
11
12
void save()
{
	ofstream myFile ("data.bin", ios::out | ios::binary);

        if (!myFile.good()) {
                cout << "Could not open data.bin for I/O." << endl;
                return;
        }

        myFile.seekp (0);
	myFile.write ((char*)&myLeader, sizeof (myLeader));
}
Thanks for that advice, but it hasn't solved the issue. No data.bin is being made.
Bump, thanks for your time.
myLeader variable is declared as static ?
it's not, should it be?
Can you show us the full code ? Could be a problem, of course ...
Main File:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
//Library Includes
#include <iostream>
#include <fstream>

//Header Includes
#include "baseUnit.h"
#include "list.h"
#include "leader.h"
#include "object.h"
//#include "mapSquares.h"
//#include "mapList.h"


using namespace std;


//Forward-declare methods.
void NewGame();  //start new game
void Continue(); //continue old game
void save(); //saves data
void gameMenu(); //Main Game Screen
list* addUnit(); //add a unit
list* addUnit(string newName); //add a unit given name

//Global Variables
list* unitsHead; //head of the Unit list
leader* myLeader;
//town* myTown;

int main()
{

	char selection='~'; //menu selection character.

	while(selection != 'q' && selection != 'Q') //Game Main Menu, until select first option. 
	{
		// display options, take selection
		cout << "New Game: N" << endl << "Continue: C (Not Implemented)" << endl << "Quit: Q" << endl;
		cin >> selection;

		//switch statement for selection
		switch(selection) 
		{
			case 'n':
			case 'N':
				NewGame();
				selection = 'q';
				break;
			case 'c':
			case 'C':
				Continue();
				selection = 'q';
				break;

			//if none above, break.
			default: break;
		}
	}

	return 0;	
}

//NOT IMPLEMENTED
void Continue()
{
	/*myLeader=new leader("blank");
	fstream myFile ("data.bin", ios::in | ios::out | ios::binary);
	myFile.seekg (0);
    myFile.read ((char*)&myLeader, sizeof (myLeader));
	cout << myLeader->getFullName();
	return;*/
}


//start a new game, initialize first troops and leader.
void NewGame()
{
	cout << "Name your Leader: ";
	string myName;
	cin >> myName;
	myLeader = new leader(myName);
	cout << "Name your Town: ";
	cin.clear();
	string myTown;
	cin >> myTown;
	//myTown= new town(myTown);
	cin.clear();
	 
	for(int x=1;x<6;x++)
	{
	cout << "Name Troop: ";
	cin >> myName;
	unitsHead=addUnit(myName);
	cin.clear();
	}


	//for(list *unit=unitsHead; unit; unit = unit->next) //parses the list

	cout << "Welcome, " << myLeader->getFullName() << endl << "Press ENTER to Continue.";
	cin.clear();cin.ignore(255, '\n');cin.get();
	save();
	gameMenu();
	
	return;
}



//game menu, after data retrieval/creation
void gameMenu()
{
	char selection='~'; //menu selection character.

	while(selection != 'q' && selection != 'Q') 
	{
		// display options, take selection
		cout << "Build: B" << endl << "Gather Resources: G" << endl << "Quit: Q" << endl;
		cin >> selection;

		//switch statement for selection
		/*switch(selection) 
		{
			
			if none above, break.
			default: break;
		}*/
	}




}




//method to save, partially copied from online tutorial.
void save()
{
	fstream myFile ("save.tes", ios::in | ios::out | ios::binary);
    
	        if (!myFile.good()) {
                cout << "Could not open data.bin for I/O." << endl;
                return;
        }

	
	myFile.seekp (0);

	myFile.write ((char*)&myLeader, sizeof (myLeader));
	myFile.close();
}
//adds a new Unit, blank template
list* addUnit()
{
	return insert(unitsHead, *(new baseUnit));
}
//adds a new Unit, given Name
list* addUnit(string newName)
{
	return insert(unitsHead, *(new baseUnit(newName)));
}



baseUnit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
//This class represents a base Unit, to be extended for more advanced Units

#ifndef BASEUNIT_H
#define BASEUNIT_H


#include <string>
using namespace std;
class baseUnit

{

	private:
		
		int age; //Unit's Age, in days.
		int health; //Unit's HP
		int attack; //Unit's Attack Power
		int defense; //Unit's Defense Power
		int loyalty; //loyalty to Leader
		string name; //Name

	public: 

		baseUnit() //default constructor, use for a blank unit template
		{
			
			age=2000; //Start at 20 years old, within a year (IMPLEMENT THE RANDOM LATER)
			health=10; //Arbitrary, Possibly Change
			attack=5; //Arbitrary, Possibly Change
			defense=5; //Arbitrary, Possibly Change
			loyalty=10; //Assume use a d20 check against a DC, +10 will give a good chance
			name="John Smith";
		}


		baseUnit(string nameSet) //default constructor, use for a blank unit template
		{
			
			age=2000; //Start at 20 years old, within a year (IMPLEMENT THE RANDOM LATER)
			health=10; //Arbitrary, Possibly Change
			attack=5; //Arbitrary, Possibly Change
			defense=5; //Arbitrary, Possibly Change
			loyalty=10; //Assume use a d20 check against a DC, +10 will give a good chance
			name=nameSet;
		}



		int getAge(){return age/100;} //returns age, in years.
		int getHealth(){return health;}
		int getAttack(){return attack;}
		int getDefense(){return defense;}
		int getLoyalty(){return loyalty;}

		string getName(){return name;} //returns name
};
#endif 


leader
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#ifndef LEADER_H
#define LEADER_H

#include <string>
using namespace std;
class leader : public baseUnit

{
	private:
		string title; //Title based on fame and morality
		int fame; //Fame variable
		int morality; // + is good, - is evil.
		int gold;
	public:
		leader(string nameSet) : baseUnit(nameSet)
		{
			fame=0;
			title="the Unknown";
			morality=0;
			gold=0;

		}

		string getFullName(){return getName()+" "+title;}
		string getTitle(){return title;}
		int getFame(){return fame;}
		int getGold(){return gold;}
		int getMorality(){return morality;}



		string saveString()
		{
			// @ signifies Leader save Line, ~ seperating between the data, strict order NAME,TITLE,FAME,MORALITY,GOLD,AGE,HEALTH,ATTACK,DEFENSE

			string returner = "@"+getName()+"~"+getTitle(); //+"~"+ //getFame(); //+"~"+getMorality()+"~"+getGold()+"~"+getAge()+"~"+getHealth()+"~"+getAttack+"~"+getDefense();
			//how convert int to string

			return returner;

		}





};



#endif 
Last edited on
1. myLeader MUST be declared as static, it is in 2 separate functions.
2. What is implementation of "leader" class or what type is it ? You used new operator ...
1
2
3
4
cout << "Name your Leader: ";
	string myName;
	cin >> myName;
	myLeader = new leader(myName);

3. You wrote this:
myFile.write ((char*)&myLeader, sizeof (myLeader));

Here sizeof operator returns most likely 4 bytes depending of platform/architecture, as myLeader is just a pointer. This is not what write() method of ofstream expects.
leader is really being used to keep track of a bunch of variables, name of the Leader, Health, etc.

I'm working on doing a basic game to try to teach myself C++, coming from Java.

Does static in C++ function different than in Java? I wasn't aware that if I used the same pointer in different methods, I'd need to declare static.

I changed leader to static and changed

myFile.write ((char*)&myLeader, sizeof (myLeader));
to
myFile.write ((char*)&myLeader, sizeof (*myLeader));

now it is giving me the error "Could not open data.bin", that was suggested I add above.
Last edited on
Bump.
no, static is not the problem. It's already global (which is not good practice (but that's another story...)).

An upcoming problem is that you cannot store complex data like that. Especially the 'std::string name' will be garbage after reading. (another side note)

the reason why you can't open the file is 'ios::in'. Then you open it for reading, but since the file doesn't exist open fails. So remove 'ios::in' and you can write that file
So how would you suggest storing the data? I know it would be possible to write it to a text file, but reading more complex stuff (especially the map system I'm working on) would be terrible...

I also removed the ios::in, and it's still not opening the file. Do I need to have that file in existence first on my computer?
Last edited on
Do I need to have that file in existence first on my computer?
Nope, I did this (data.bin didn't exists for sure)
1
2
3
  fstream myFile ("data.bin", ios::out | ios::binary);
  if(myFile.good())
    myFile.write("123", 3);
and it worked. So I suggest that you make a new project containing just this and add more and more stuff to it and see at what point it doesn't work anymore.

So how would you suggest storing the data?
It depends on the requirements to the data. Maybe a simple database would do?
For most of the data, it's just int's and Strings.

The problem would be for the map, which (at least currently) I was planning on using as a semi-linked list of "MapSquares", where mapsquare holds a bunch of different ints, and possibly a object of type TOWN, which itself holds a bunch of ints.

The semi-linked list would function as a normal linked list, but more like a map, I.E. pointers to NSEW, which are the next MAPSQUARE in that direction.
So better use the stream mechanism. You can overload the << and >> operator like so:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class TOWN
{
...
  friend std::ostream &operator<<(std::ostream &os, const TOWN &t);
  friend std::istream &operator>>(std::istream &is, TOWN &t); // EDIT: no const
...
};
std::ostream &operator<<(std::ostream &os, const TOWN &t)
{
  os << t.val1 << ' ' << t.val2 << ' '; // EDIT: When writing add space between the elements!
  return os;
 }
std::istream &operator>>(std::istream &is, TOWN &t) // EDIT: no const
{
  is >> t.val1 >> t.val2;
  return is;
}
...
{
  TOWN mytown(...);
...
  fstream myFile ("data.bin");
  myFile << mytown;
...
  myFile >> mytown;
}


For containers (like map) first store/read the amount of elements. This applies also for linked lists since you cannot store pointers. This way it'd be easier to deal with those arbitrary complex data. Be aware that you can only read the data when your internal structure didn't change. So you may store something at the very beginning which hints what kind of data it is.
Last edited on
Topic archived. No new replies allowed.