My displayRecord()
function runs correctly. It will display the given record, then pause for a second, but then the program closes before the function returns to main()
.
After debugging, I received this error:
Exception thrown: read access violation.
_Val was 0x19B6C345688.
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
using namespace std;
struct Item
{
string description;
int quantity;
double wCost;
double rCost;
string dateAdded;
};
int addRecord();
int displayRecord();
int changeRecord();
int main()
{
cout << "Inventory Program" << endl;
cout << "-------------------------------" << endl << endl;
char again = 'y';
int selection = 0;
do
{
cout << "MENU" << endl;
cout << "----" << endl;
cout << "1. Add a record" << endl;
cout << "2. Display a record" << endl;
cout << "3. Change a record" << endl;
cout << "4. Exit Program" << endl << endl;
do
{
cout << "Please enter a selection from the menu (1, 2, 3, or 4): ";
cin >> selection;
if (selection != 1 && selection != 2 && selection != 3 && selection != 4)
{
cout << "\nYou must enter a valid selection of either 1, 2, 3, or 4. Please try again.\n\n";
}
} while (selection != 1 && selection != 2 && selection != 3 && selection != 4);
if (selection == 4)
{
cout << "Exiting Program..." << endl;
cin.clear();
return 0;
}
if (selection == 1)
{
if (addRecord() == 1)
{
return 1;
}
}
else if (selection == 2)
{
if (displayRecord() == 1)
{
return 1;
}
}
else
{
if (changeRecord() == 1)
{
return 1;
}
}
cout << "Return to menu? (Type Y for yes): ";
cin >> again;
} while (again == 'y' || again == 'Y');
cout << "-------------------------------" << endl << endl;
system("pause");
return 0;
}
int displayRecord()
{
fstream inventoryFile("inventory.dat", ios::in | ios::binary);
if (!inventoryFile)
{
cout << "ERROR: Cannot open file. Aborting Program." << endl;
return 1;
}
Item item;
long recNum = 0;
cout << "\nEnter the record number you wish to display: ";
cin >> recNum;
inventoryFile.seekg((recNum - 1) * sizeof(item), ios::beg);
inventoryFile.read(reinterpret_cast<char*>(&item), sizeof(item));
if (!inventoryFile)
{
cout << "\nERROR: Record does not exist." << endl << endl;
inventoryFile.close();
return 0;
}
else
{
cout << endl;
cout << showpoint << setprecision(2) << fixed << endl;
cout << "Item Description: " << item.description << endl;
cout << "Quantity : " << item.quantity << endl;
cout << "Wholesale Cost : " << item.wCost << endl;
cout << "Retail Cost : " << item.rCost << endl;
cout << "Date Added: : " << item.dateAdded << endl << endl;
inventoryFile.clear();
}
inventoryFile.close();
return 0;
}
I have another branch where I open the file in main()
and pass the file to the function, but the same thing happens.
I have used clear()
and seekg()
to set the position at the beginning of the file.
Your Item
type is non-trivial, as it contains non-trivial std::string
members. As such, you cannot use istream::read()
and ostream::write()
to read/write Item
objects in binary mode the way you are doing.
std::string
contains a pointer to its character data, which may be (and usually is) located elsewhere in memory, outside of the std::string
object. You are reading/writing the internal state of the std::string
objects, not their character data. So, when you write a file, then restart the program, and then read the file, you will not end up with valid string objects.
To read/write your items in binary mode, they need to be of a trivial type. And to facilitate the random seeking that your code is using, those items need to be a fixed size. Which means, you will have to use fixed-length char[]
arrays for your strings, eg:
struct Item
{
char description[256];
int quantity;
double wCost;
double rCost;
char dateAdded[32];
};
Otherwise, if you want to read/write std::string
values then you must (de-)serialize the Item
objects instead (which is not kind to random seeking), eg:
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <cstdint>
#include <cstdio>
using namespace std;
struct Item
{
string description;
int quantity;
double wCost;
double rCost;
string dateAdded;
};
template<typename T>
istream& read(istream &in, T &value)
{
return in.read(reinterpret_cast<char*>(&value), sizeof(value));
}
template<typename T>
ostream& write(ostream &out, const T &value)
{
return out.write(reinterpret_cast<const char*>(&value), sizeof(value));
}
istream& read(istream &in, string &value)
{
uint32_t size;
if (in.read(reinterpret_cast<char*>(&size), sizeof(size)))
{
value.resize(size);
in.read(value.data(), size);
}
return in;
}
ostream& write(ostream &out, const string &value)
{
uint32_t size = value.size();
if (out.write(reinterpret_cast<char*>(&size), sizeof(size)))
out.write(value.c_str(), size);
return out;
}
istream& operator>>(istream &in, Item &item)
{
read(in, item.description);
read(in, item.quantity);
read(in, item.wCost);
read(in, item.rCost);
read(in, item.dateAdded);
return in;
}
ostream& operator<<(ostream &out, const Item &item)
{
write(out, item.description);
write(out, item.quantity);
write(out, item.wCost);
write(out, item.rCost);
write(out, item.dateAdded);
return out;
}
...
int addRecord()
{
Item item;
...
ofstream inventoryFile("inventory.dat", ios::binary | ios::app);
if (!inventoryFile)
{
cout << "ERROR: Cannot open file. Aborting Program." << endl;
return 1;
}
if (!(inventoryFile << item))
{
cout << "ERROR: Cannot write to file. Aborting Program." << endl;
return 1;
}
return 0;
}
int displayRecord()
{
ifstream inventoryFile("inventory.dat", ios::binary);
if (!inventoryFile)
{
cout << "ERROR: Cannot open file. Aborting Program." << endl;
return 1;
}
long recNum = 0;
cout << "\nEnter the record number you wish to display: ";
cin >> recNum;
Item item;
while (recNum > 1)
{
if (!(inventoryFile >> item))
{
cout << "\nERROR: Record does not exist." << endl << endl;
return 0;
}
---recNum;
}
if (!(inventoryFile >> item))
{
cout << "ERROR: Cannot read from file. Aborting Program." << endl;
return 1;
}
cout << endl;
cout << showpoint << setprecision(2) << fixed << endl;
cout << "Item Description: " << item.description << endl;
cout << "Quantity : " << item.quantity << endl;
cout << "Wholesale Cost : " << item.wCost << endl;
cout << "Retail Cost : " << item.rCost << endl;
cout << "Date Added: : " << item.dateAdded << endl << endl;
return 0;
}
int changeRecord()
{
ifstream inventoryFile("inventory.dat", ios::binary);
if (!inventoryFile)
{
cout << "ERROR: Cannot open file. Aborting Program." << endl;
return 1;
}
ofstream inventoryTemp("inventory.tmp", ios::binary);
if (!inventoryTemp)
{
cout << "ERROR: Cannot create file. Aborting Program." << endl;
return 1;
}
long recNum = 0;
cout << "\nEnter the record number you wish to change: ";
cin >> recNum;
Item item;
while (recNum > 1)
{
if (!(inventoryFile >> item))
{
cout << "\nERROR: Record does not exist." << endl << endl;
inventoryTemp.close();
remove("inventory.tmp");
return 0;
}
if (!(inventoryTemp << item))
{
cout << "ERROR: Cannot write to file. Aborting Program." << endl;
inventoryTemp.close();
remove("inventory.tmp");
return 1;
}
---recNum;
}
if (!(inventoryFile >> item))
{
cout << "ERROR: Cannot read from file. Aborting Program." << endl;
inventoryTemp.close();
remove("inventory.tmp");
return 1;
}
// display item...
// ask user for new values...
if (!(inventoryTemp << item))
{
cout << "ERROR: Cannot write to file. Aborting Program." << endl;
inventoryTemp.close();
remove("inventory.tmp");
return 1;
};
while (inventoryFile >> item)
{
if (!(inventoryTemp << item))
{
cout << "ERROR: Cannot write to file. Aborting Program." << endl;
inventoryTemp.close();
remove("inventory.tmp");
return 1;
}
}
if (!inventoryFile.eof())
{
cout << "ERROR: Cannot read from file. Aborting Program." << endl;
inventoryTemp.close();
remove("inventory.tmp");
return 1;
}
inventoryFile.close();
inventoryTemp.close();
remove("inventory.bak");
rename("inventory.dat", "inventory.bak");
rename("inventory.tmp", "inventory.dat");
return 0;
}