Program crashing when reading from file

For my Advanced C++ class, I am writing a program that uses a file to store information about inventory objects, and will eventually allow for the objects to be edited, added, and removed. However, I have run into a bit of a problem with my early implementation. So far, I have the ability to create a blank file capable of storing up to 100 inventory items, the ability to display what, if anything, is currently in the file, and the ability to add new items to the file.

Now comes the tricky part. When I create a new file (overwriting the existing one), I can display the list of items as often as I want (all I see is the column headers and nothing else). As soon as I add an item (or items), I can display the list of items once. If I try to display it again, the program will show the list, then crash. By "crash," I mean I get the Windows popup saying the program has stopped working. If I run the program using an existing file, it crashes as soon as I ask it to display the current inventory.

Somehow I think I am corrupting my file, but I'm not sure how that is happening. I suspect it is something in either the displayLine or displayFile function.

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
164
165
166
167
168
169
170
171
172
#include "Inventory.h"
#include <fstream>
#include <iostream>
#include <iomanip>

using std::ofstream;
using std::ifstream;
using std::fstream;
using std::ostream;
using std::ios;
using std::cerr;
using std::endl;
using std::cout;
using std::cin;
using std::left;
using std::right;
using std::setw;
using std::setprecision;
using std::fixed;
using std::getline;

void createFile();
void displayFile();
void displayLine(ostream &output, Inventory &tools);
void addTool();

enum Menu {DISPLAY = 1, ADJUST, ADD, DELETE, CREATE, EXIT};	// Enumeration list for menu options.

int main()
{
	int selection = 0;

	do
	{
		cout << "\nMAIN MENU" << endl;
		cout << "Please make a selection from the following options:" << endl;
		cout << "1) Display Inventory" << endl;
		cout << "2) Adjust Current Inventory" << endl;
		cout << "3) Add New Tool to Inventory" << endl;
		cout << "4) Delete Tool from Inventory" << endl;
		cout << "5) Create Blank Inventory File (WARNING! This will completely erase any existing inventory file)" << endl;
		cout << "6) Exit" << endl << endl;
		cin >> selection;

		switch (selection)
		{
			char sure;
		case DISPLAY:
			displayFile();
			break;
		case ADJUST:
			break;
		case ADD:
			addTool();
			break;
		case DELETE:
			break;
		case CREATE:
			cout << "Are you sure you want to erase any existing data and create a new inventory file (y/n)?";	// Make sure user is aware that creating a new file will erase any existing data.
			cin >> sure;
			if ((sure == 'y') || (sure == 'Y'))	// If user indicates they want to create a new file...
				createFile();	// ...create new file.
			break;
		case EXIT:
			break;
		default:
			cerr << "Invalid selection." << endl;
			break;
		}

	} while (selection != EXIT);

	return 0;
}

void createFile()
{
	ofstream outFile("hardware.dat", ios::out | ios::binary);	// Open file for output.

	if (!outFile)	// If file could not be opened...
	{
		cerr << "File could not be opened." << endl;	// ...display error message...
		exit(1);	// ...and end the program.
	}

	Inventory blankInventory;	// Create blank Inventory object.

	for (int i = 0; i < 100; i++)
		outFile.write(reinterpret_cast<const char *>(&blankInventory), sizeof(Inventory));	// Write 100 copies of blank inventory object to the file.

	outFile.close();	// Close the file.
}

void displayFile()
{
	ifstream inFile("hardware.dat", ios::in | ios::binary);	// Open file for input.

	if (!inFile)	// If file could not be opened...
	{
		cerr << "File could not be opened." << endl;	// ...display error message...
		exit(1);	// ...and end the program.
	}

	cout << endl << left << setw(10) << "Record #" << setw(17) << "Tool name" << setw(10) << "Quantity" << setw(7) << right << "Cost" << endl;	// Display column headers.

	Inventory tools;	// Create Inventory object.

	inFile.read(reinterpret_cast<char *>(&tools), sizeof(Inventory));	// Read first entry in file.

	while (inFile && !inFile.eof())	// Loop while no errors and not EOF.
	{
		if (tools.getRecordNum() != 0)	// If the record number for the entry is not equal to 0...
			displayLine(cout, tools);	// ...display that entry.

		inFile.read(reinterpret_cast<char *>(&tools), sizeof(Inventory));	// Read next entry in file.
	}

	inFile.close();	// Close the file.
}

void displayLine(ostream &output, Inventory &tools)
{
	output << left << setw(10) << tools.getRecordNum() 
		<< setw(17) << tools.getToolName() 
		<< setw(10) << tools.getQuantity() 
		<< right << setw(7) << fixed << setprecision(2) << tools.getCost() << endl;	// Display entry from file.
}

void addTool()
{
	int newRecordNumber;
	string newToolName;
	int newQuantity;
	double newCost;
	Inventory newTool;	// Create new Inventory object.

	fstream addToFile("hardware.dat", ios::in | ios::out | ios::binary);	// Open file for input and output.

	if (!addToFile)	// If file could not be opened...
	{
		cerr << "File could not be opened." << endl;	// ...display error message...
		exit(1);	// ...and end the program.
	}

	cout << "Enter new tool record number (1 - 100, 0 to end): ";	// Ask for record number for new tool.
	cin >> newRecordNumber;

	while (newRecordNumber > 0 && newRecordNumber <= 100)
	{
		cout << "Enter new tool name: ";	// Ask for name of new tool.
		cin.sync();
		getline(cin, newToolName);
		cout << "Enter new tool quantity: ";	// Ask for quantity of new tool.
		cin >> newQuantity;
		cout << "Enter new tool cost: ";	// Ask for cost of new tool.
		cin >> newCost;

		newTool.setRecordNum(newRecordNumber);	// Store data in Inventory object.
		newTool.setToolName(newToolName);
		newTool.setQuantity(newQuantity);
		newTool.setCost(newCost);

		addToFile.seekp((newTool.getRecordNum() - 1) * sizeof(Inventory));	// Find location for new tool's record number in the file.

		addToFile.write(reinterpret_cast<const char *>(&newTool), sizeof(Inventory));	// Write data from Inventory object to the file.

		cout << "Enter another new tool record number (1 - 100, 0 to end): ";	// Ask for record number of another new tool, or end the loop.
		cin >> newRecordNumber;
	}

	addToFile.close();	// Close the file.
}
Does Inventory contain std::string members? If it does, lines 89, 108, 115, and 165 will never do what you expect, because std::string contains pointers to the heap, which can't be saved to a file. You have to find a way to serialize the strings manually.
For example, "Hello, World!\n" could be serialized as 00 0E 48 65 6C 6C 6F 2C 20 57 6F 72 6C 64 21 0A (000E is the size of the string).
Inventory does have one std::string member, the name of the tool. I wasn't aware of the problem with strings, though in hindsight it should have raised at least one red flag since strings don't actually have a fixed length and I am counting on the Inventory objects to all be the same size. I'll change that data member to a fixed-length char array and see if that resolves the problem.

@helios, thanks for the info and idea.
Yep, it was indeed a problem with the string data member. Once I changed it to a fixed-size char array, the problem disappeared. At least, it seems to have disappeared.

Thanks again for pointing my thought process in the right direction, helios.
Topic archived. No new replies allowed.