Writing object array contents to and from file - c++

I am currently trying to write a section of my program to read objects from an array into a text file and vice versa. I can get it to output objects to a file with seemingly no issues, but when I try to read data in from the text file into an empty array, it places the last object where the first one should be and leaves all others blank. Where am I going wrong? Here is my class code
//Defining function for items to file
void Stock::writeToFile(ofstream& fileOut)
{
fileOut << stockCode << " ";
fileOut << stockDesc << " ";
fileOut << currentLevel << " ";
fileOut << reorderLevel << " ";
}
//Defining function for reading items in from the file
void Stock::readFromFile(ifstream& fileIn)
{
while (fileIn >> stockCode >> stockDesc >> currentLevel >> reorderLevel)
{
fileIn >> stockCode;
fileIn >> stockDesc;
fileIn >> currentLevel;
fileIn >> reorderLevel;
}
}
And this is my code in main
#include <iostream>
#include <string>
#include <fstream>
#include "Stock.h"
using namespace std;
int main()
{
Stock items[4];
int option = 0;
cout << "1.Display full stock list." << endl;
cout << "Please pick an option: ";
cin >> option;
switch (option)
{
case 1:
cout << "stockCode" << '\t' << "stockDesc" << '\t' << '\t' << "CurrentLevel" << '\t' << "ReorderLevel" << endl;
cout << "------------------------------------------------------------------------------" << endl;
ifstream fileIn;
fileIn.open("Stock.txt");
for (int i = 0; i < 4; i++)
{
items[i].readFromFile(fileIn);
cout << items[i].getCode() << '\t' << '\t';
cout << items[i].getDescription() << '\t' << '\t' << '\t';
cout << items[i].getCurrentLevel() << '\t' << '\t';
cout << items[i].getReorderLevel() << endl;
}
}
return 0;
}

This loop runs through the entire file until it can't read any more, which is why the last set of variables is only one visible. All the ones prior are overwritten.
while (fileIn >> stockCode >> stockDesc >> currentLevel >> reorderLevel)
{
fileIn >> stockCode;
fileIn >> stockDesc;
fileIn >> currentLevel;
fileIn >> reorderLevel;
}
First time in your for loop, the call
items[i].readFromFile(fileIn);
cycles through the entire file. All the remaining iterations in your for loop, try to read from the file, but it's already at EOF.

As molbdnilo pointed out you want to read from file into your stock object sequentially so the loop should be removed. Also in this case it is better to have custom friend functions to read and write from streams into your object directly. See code below that can accomplish that.
#include <iostream>
#include <fstream>
#include <string>
// This what goes into your "Stock.h"
class Stock{
std::string stockCode;
std::string stockDesc;
int currentLevel;
int reorderLevel;
public:
Stock():currentLevel(0),reorderLevel(0){};
Stock(std::string const & scode,
std::string const & sdesc,
int const clevel,
int const rlevel
):stockCode(scode),
stockDesc(sdesc),
currentLevel(clevel),
reorderLevel(rlevel){}
friend std::istream& operator >>(std::istream& is, Stock& stk)
{
if (is) {
is
>> stk.stockCode
>> stk.stockDesc
>> stk.currentLevel
>> stk.reorderLevel;
}
return is;
}
friend std::ostream& operator <<(std::ostream& os, Stock const& stk)
{
os
<< stk.stockCode << ' '
<< stk.stockDesc << ' '
<< stk.currentLevel << ' '
<< stk.reorderLevel
<< '\n' //Line break after every object so that you can open and read.
;
}
};
//This is your main code with the read loop fixed.
int main()
{
const int N = 4;
Stock items[N];
//1. Create 4 Stock objects.
items[0] = Stock("A", "STKA", 100, 100);
items[1] = Stock("B", "STKB", 101, 101);
items[2] = Stock("C", "STKC", 102, 102);
items[3] = Stock("D", "STKD", 103, 103);
//2. Write the 4 Stock objects to a file.
std::ofstream ofs;
ofs.open("Stock.txt", std::ofstream::out);
for ( int i = 0; i < N ; ++i ) {
ofs << items[i] ;
}
ofs.close();
// 3. Read from the file written in 2. and print.
std::ifstream fileIn;
fileIn.open("Stock.txt");
Stock stk;
while (fileIn >> stk) {
std::cout << stk;
}
fileIn.close();
}
And the output is:
A STKA 100 100
B STKB 101 101
C STKC 102 102
D STKD 103 103

You're reading the entire file into the first Stock, and you're even reading each Stock's data twice.
The remainder of the stocks fail because you've reached the end of the file.
Remove the loop from readFromFile.

For each item, you are looping until the end of file is reached in Stock::readFromFile(). You need to remove the loop there:
//Defining function for reading items in from the file
void Stock::readFromFile(ifstream& fileIn)
{
fileIn >> stockCode;
fileIn >> stockDesc;
fileIn >> currentLevel;
fileIn >> reorderLevel;
}
So, there was nothing left to read for the remaining items.

Related

Getting the input from the file as string and also integer C++

I have a text file. I have a text file consisting of member data. I am developing a program where we can get the member data from the file. After searching in the internet, I have searched a way to read all the data from the file as char array. But I want to change it where upon reading from file I want the data to be string and also integer.
name, icno, email, phone_number, acc_num, password ( read from file AS STRING )
month, year ( read from file AS INTEGER )
Content of Membership.txt
Mathavan|021127100897|MathavanKrishnan27#gmail.com|0167750575|1410065449|Mathavan1234|3|2022
Mathavan|021127100897|MathavanKrishnan27#gmail.com|0167750575|1410065448|Mathavan1234|3|2024
Mathavan|021127100897|MathavanKrishnan27#gmail.com|0167750575|1410065447|Mathavan1234|3|2022
string member_login(){
title();
fstream member;
member.open("Membership.txt",ios::in);
string pass_input, line, acc_num1, password1;
int login_attempt = 0, count = 0 , account = 0;
char dummy, resp, accno_input[25], name[25], icno[25],email [40], phone_number[25],acc_num[25],password[25],month[25], year[25];
account_num:
cout << " Enter your account number : ";
cin >> accno_input;
ifstream file("Membership.txt");
while (!file.eof()){
getline(file, line);
count++;
}
cout << accno_input;
int i = 0;
while(i <= count)
{
member.getline(name,25,'|');
member.getline(icno,25,'|');
member.getline(email,40,'|');
member.getline(phone_number,25, '|');
member.getline(acc_num,25, '|');
member.getline(password,25,'|' );
member.getline(month,25,'|' );
member.getline(year, 25);
cout << name << " ";
cout << icno << " ";
cout << acc_num << " ";
cout << accno_input;
if (acc_num == accno_input){
account = 1;
break;
}
i ++;
}
cout << account;
member.close();
if ( account != 1 ){
cout << endl;
cout << " Your account not found !!!"<< endl;
cout << " Please try again !!" << endl << endl;
cout << " PLEASE ENTER ANY KEY TO CONTINUE >>> ";
cin >> dummy;
goto account_num;
}
password1 = password;
cout << endl;
cout << " Enter your account password : ";
cin >> pass_input;
for (login_attempt = 1 ; login_attempt <= 2 ; login_attempt ++){
if (pass_input == password1){
cout << "Login Successful !!!";
break;
}
cout << endl;
cout << "Login Failed. Attempt " << login_attempt << " of 3" << endl;
cout << "Please re-enter Password: " ;
cin >> pass_input;
if (pass_input == password1){
cout << "Login Successful !!!";
break;
}
}
if ( login_attempt == 3){
cout << endl;
cout << "Login Failed. Attempt 3 of 3";
}
return accno_input;
}
There are so many things completely wrong in your program that I do recomend to you:
Delete and start from scratch.
There is no meaningful fix possible. There is even a goto. And you MUST stop using C-Style arrays with some agic dimension in C++. And C++ has many things to make your live easier. Simply use them.
Please find below a C++ solution.
You can copy and paste it and stay as you are, or, you take 3 hours and google all constructs and try to understand and learn and become a better programmer. Your choise.
#include <iostream>
#include <fstream>
#include <string>
#include <iomanip>
#include <vector>
#include <regex>
#include <iterator>
#include <algorithm>
const std::regex re{ R"(\|)" };
struct Member {
// Memeber data
std::string name{};
std::string icno{};
std::string email{};
std::string phoneNumber{};
std::string accountNumber{};
std::string password{};
int month{};
int year{};
// Extractor operator
friend std::istream& operator >> (std::istream& is, Member& m) {
// Readone complete line
if (std::string line{}; std::getline(is, line)) {
// Split it into parts
std::vector parts(std::sregex_token_iterator(line.begin(), line.end(), re, -1), {});
// assign parts to member data
if (parts.size() == 8) {
m.name = parts[0]; m.icno = parts[1]; m.email = parts[2]; m.phoneNumber = parts[3]; m.accountNumber = parts[4]; m.password = parts[5];
m.month = std::stoi(parts[6]); m.year = std::stoi(parts[7]);
}
}
return is;
}
};
// Filename for member data
const std::string fileName{ "r:\\Membership.txt" };
int main() {
// Open the data file and check, if it could be opened
if (std::ifstream fileStream{ fileName }; fileStream) {
// Read complete source file, parse it and get all data
std::vector memberData(std::istream_iterator<Member>(fileStream), {});
// We want the user to give 3 trials to enter valid data
constexpr unsigned int MaxTrials = 3u;
unsigned int numberOfTrials{};
// A valid input will stop the loop immediately
bool validInputgiven{};
// Now, try to get the correct input
while (not validInputgiven and numberOfTrials < MaxTrials) {
// Get an acoount number
std::cout << "\nEnter a account number: ";
std::string account{};
std::cin >> account;
// Check, if the account number is in the member data
if (std::count_if(memberData.begin(), memberData.end(), [&](const Member& m) { return m.accountNumber == account; }) > 0) {
// Account info wasOK. Get the password
std::cout << "\nEnter your password: ";
std::string password{};
std::cin >> password;
if (std::count_if(memberData.begin(), memberData.end(), [&](const Member& m) { return m.accountNumber == account and m.password == password; }) > 0) {
// Valid data found
validInputgiven = true;
std::cout << "\n\nEverything OK. Data validated.\n\n";
}
}
// Next try
++numberOfTrials;
if (not validInputgiven and numberOfTrials < MaxTrials) std::cout << "\nInvalid input. Please try again\n\n\n";
}
if (not validInputgiven ) std::cout << "\nToo many wrong tries. Aborting . . .\n\n\n";
}
else std::cerr << "\n\nError. Could not open source file '" << fileName << "'\n\n";
}

Exception thrown at 0x5914F3BE (ucrtbased.dll)

I have some code that takes a list of names + double values from a .txt file and displays these in the command prompt. For this an array of structs is dynamically allocated. The code should know the size of the array based on the first value in the .txt file, which is then followed by the names and associated values. It should then display the list in two parts with names that have an associated double value higher than or equal to 10.000 listed first. If none of the values qualifies, it displays 'None' in the first half.
The program executes, but the debugger gives an exception and the output is not as expected.
#include <iostream>
#include <fstream>
#include <string>
#include <cstdlib>
using namespace std;
struct donor
{
string name;
double contribution = 0;
};
int main()
{
string filename;
ifstream inFile;
cout << "Enter name of data file: ";
cin >> filename;
inFile.open(filename);
cin.clear();
if(!inFile.is_open())
{
cout << "Could not open the file " << filename << endl;
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
cin.clear();
donor* dlist = new donor[amount];
int i;
while(inFile.good())
{
for(i = 0; i < amount; i++)
{
getline(inFile, dlist[i].name);
cin.clear();
inFile >> dlist[i].contribution;
cin.clear();
}
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons = false;
for(i = 0; i < amount; i++)
{
if(dlist[i].contribution >= 10000)
{
grandpatrons = true;
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
if(grandpatrons == false)
{
cout << "None" << endl;
}
cout << "Here's the list of Patrons:\n";
for (i = 0; 1 < amount; i++)
{
if (dlist[i].contribution < 10000)
{
cout << dlist[i].name << endl;
cout << dlist[i].contribution << endl;
}
}
delete[] dlist;
return 0;
}
The donorlist.txt file looks like this:
4
Bob
400
Alice
11000
But the output looks like this:
Enter name of data file: donorlist.txt
Here's the list of Grand Patrons:
None
Here's the list of Patrons:
0
0
0
0
The exception that the debugger gives me is:
Exception thrown at 0x5914F3BE (ucrtbased.dll) in 6_9.exe: 0xC0000005: Access violation reading location 0xA519E363.
Now I assume something is going wrong with reading from the dynamically allocated memory. Maybe something is causing me to read from memory beyond the allocated array? I'm having trouble finding exactly where the mistake is being made.
Your problems begin with the wrong amount written in your data file.
Fix it with:
2
Bob
400
Alice
11000
They then continue with the fact that you inccorectly read the file.
Remember: Mixing operator>> and getline() is not as simple as it seems.
You see, operator>> IGNORES newline and space characters until it finds any other character.
It then reads the upcoming characters until it encounters the next newline or space character, BUT DOES NOT DISCARD IT.
Here is where the problem with getline comes in. getline reads EVERYTHING until it encounters newline or a specified delim character.
Meaning, that if your operator>> stops after encountering newline, getline will read NOTHING since it immediately encounters newline.
To fix this, you need to dispose of the newline character.
You can do this by first checking if the next character in the stream is indeed newline and then using istream::ignore() on it;
int next_char = stream.peek();
if(next_char == '\n'){
stream.ignore();
}
A working example of your code would be:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
//Suggestion: class/struct names should start with a capital letter.
struct Donor{
//Suggestion: Use member initializer lists to specify default values.
Donor() : name(), contribution(0){}
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
//Suggestion: Open the file immediately with the filename and use `operator bool` to check if it opened.
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount; //! Leaves '\n'
Donor* donors = new Donor[amount];
for(int i = 0; i < amount; ++i){
switch(inFile.peek()){
case '\n': inFile.ignore();
break;
case EOF: cout << "Donor amount too big!\n";
exit(EXIT_FAILURE);
}
getline(inFile, donors[i].name);
inFile >> donors[i].contribution;
}
cout << "Here's the list of Grand Patrons:\n";
bool grandpatrons_exist = false;
for(int i = 0; i < amount; ++i){
if(donors[i].contribution >= 10000){
grandpatrons_exist = true;
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
if(!grandpatrons_exist){
cout << "None\n";
}
cout << "Here's the list of Patrons:\n";
for(int i = 0; 1 < amount; ++i){
if(donors[i].contribution < 10000){
cout << donors[i].name << '\n';
cout << donors[i].contribution << '\n';
}
}
delete[] donors;
return 0;
}
Now, an even better solution would be to use vectors instead of raw pointers and implement operator>> and operator<< which would greatly simplify
the reading and printing of the objects.
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
class Donor{
public:
Donor() noexcept: name(), contribution(0){}
friend istream& operator>>(istream& stream, Donor& donor){
switch(stream.peek()){
case EOF: return stream;
case '\n': stream.ignore();
}
getline(stream, donor.name);
stream >> donor.contribution;
return stream;
}
friend ostream& operator<<(ostream& stream, const Donor& donor){
stream << donor.name << ' ' << donor.contribution;
return stream;
}
const string& get_name() const noexcept{
return name;
}
const double& get_contribution() const noexcept{
return contribution;
}
private:
string name;
double contribution;
};
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
int amount;
inFile >> amount;
vector<Donor> donors(amount);
//Read it as `for donor in donors`
for(Donor& donor : donors){
inFile >> donor;
}
//An STL function that takes a lambda as the thirs argument. You should read up on them if you haven't.
//I would prefer using this since it greatly improves readability.
//This isn't mandatory, your implementation of this part is good enough.
bool grandpatrons_exist = any_of(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(grandpatrons_exist){
for(const Donor& donor : donors){
if(donor.get_contribution() >= 10000){
cout << donor << '\n';
}
}
}
else{
cout << "None\n";
}
cout << "\nHere's the list of Patrons:\n";
for(const Donor& donor : donors){
if(donor.get_contribution() < 10000){
cout << donor << '\n';
}
}
return 0;
}
Some other great improvements would be:
Use partition to seperate great patrons from normal ones.
Use stream iterators to read the objects into the vector.
int main(){
cout << "Enter the filename: ";
string filename;
cin >> filename;
ifstream inFile(filename);
if(!inFile){
cout << "Could not open the file " << filename << '\n';
cout << "Program terminating.\n";
exit(EXIT_FAILURE);
}
//Ignore the first line completely
inFile.ignore(numeric_limits<streamsize>::max(), '\n');
//Calls `operator>>` internally
vector<Donor> donors(istream_iterator<Donor>{inFile}, istream_iterator<Donor>{});
auto first_grand_patron = partition(begin(donors), end(donors), [](const Donor& donor){ return donor.get_contribution() >= 10000; });
cout << "Here's the list of Grand Patrons:\n";
if(first_grand_patron == begin(donors)){
cout << "None!\n";
}
for(auto patron = begin(donors); patron != first_grand_patron; ++patron){
cout << *patron << '\n';
}
cout << "\nHere's the list of Patrons:\n";
for(auto patron = first_grand_patron; patron != end(donors); ++patron){
cout << *patron << '\n';
}
return 0;
}
Now some general tips:
Struct/Class names should start with a capital letter.
Stop Using std::endl.
No need to cin.clear(). Cin is only used once and never again.
Use member-initializer lists.
Optionally use ++i instead of i++ in for loops to get used to the correct way of incrementing a variable unless needed otherwise.
bool grandpatrons is too much of an abstract name for a flag.
donors is a subjectively better name than short for donor list.

Vector Function program issues

#include<iostream>
#include<string>
#include<fstream>
#include<vector>
using namespace std;
void check(ifstream &iFile)
{
if (!iFile.is_open())
{
cout << "Data file not found!" << endl;
system("pause");
exit(1); // exit the program if the file is not found.
}
}
void readIn(ifstream &iFile, vector<string> &fName, vector<string> &lName, vector<string> &jTitle, vector<string> &eID, vector<double> &hoursWorked, vector<double> &wage, vector<int> &deductions, vector<double> &sPay, string sTemp, double dTemp, int iTemp)
{
while (!iFile.eof())
{
iFile >> sTemp;
fName.push_back(sTemp);
iFile >> sTemp;
lName.push_back(sTemp);
iFile.ignore();
getline(iFile, sTemp);
jTitle.push_back(sTemp);
iFile >> sTemp;
eID.push_back(sTemp);
iFile >> dTemp;
hoursWorked.push_back(dTemp);
iFile >> dTemp;
wage.push_back(dTemp);
iFile >> iTemp;
deductions.push_back(iTemp);
iFile >> dTemp;
sPay.push_back(dTemp);
}
cout << "completed" << endl;
}
int main()
{
ifstream iFile;
iFile.open("data.txt");
check(iFile);
vector<string> fName, lName, eID, eStatus, jTitle;
vector<double> nPay, gPay, oPay, oHours;
vector<double> hoursWorked, wage, sPay;
vector<int> deductions;
// temporary names to pass to the vector
string sTemp; // string temp
double dTemp=0; // double temp
int iTemp=0; // integar temp
readIn(iFile, fName, lName, jTitle, eID, hoursWorked, wage, deductions, sPay, sTemp, dTemp, iTemp);
/* while (!iFile.eof())
{
iFile >> sTemp;
fName.push_back(sTemp);
iFile >> sTemp;
lName.push_back(sTemp);
iFile.ignore();
getline(iFile, sTemp);
jTitle.push_back(sTemp);
iFile >> sTemp;
eID.push_back(sTemp);
iFile >> dTemp;
hoursWorked.push_back(dTemp);
iFile >> dTemp;
wage.push_back(dTemp);
iFile >> iTemp;
deductions.push_back(iTemp);
iFile >> dTemp;
sPay.push_back(dTemp);
}*/
int sizeOf = fName.size();
for (int a = 0; a < sizeOf; a++)
{
cout << fName.size() << " FName " << fName[a] << " LName " << lName[a] << " JobTitle " << jTitle[a] << endl;
cout << "EmployeeID " << eID[a] << " Hours Worked " << hoursWorked[a] << " Hourly Wage " << wage[a] << endl;
cout << "Deductions " << deductions[a] << " Salary Pay " << sPay[a] << endl;
}
system("pause");
return 0;
}
I'm running into an issue where my function will not do anything. It will compile, but there is no output. The thing is when I take out the vector sPay from all the parts it works perfectly fine. Any suggestions as to why that one part isn't working? From my limited knowledge it should work perfectly fine, however I can't figure out what would be causing this.
My example text file is
Alan
WakeField
IT GUY
T2034
40
15
1
Hourly
0.00
Your input file does not match your reading code. There are 9 values in the file that you have shown, but your code is attempting to read only 8 values.
When readIn() gets to this code:
iFile >> dTemp;
sPay.push_back(dTemp);
It attempt to read a double but the file has Hourly instead, so the read fails.
So, either remove the Hourly line from the file, or else add a call to iFile >> sTemp to read that line.
Also, the parameters string sTemp, double dTemp, and int iTemp should be declared as local variables instead of as input parameters.
Also, readIn() is not doing any error handling. Your main() code makes an invalid assumption that the vector of first names exactly matches the size of the other vectors, but readIn() does not guarantee that.
And lastly, checking eof() before you have read anything is wrong. The stream's eofbit flag is not updated until a read operation attempts to read past EOF.
You should consider re-writing this code. For example, try something more like this:
#include <iostream>
#include <string>
#include <fstream>
#include <vector>
struct Employee
{
std::string fName;
std::string lName;
std::string title;
std::string eID;
double hoursWorked;
double wage;
int deductions;
std::string wageType;
double sPay;
Employee() :
hoursWorked(0), wage(0), deductions(0), sPay(0)
{
}
};
void check(std::ifstream &iFile)
{
if (!iFile.is_open())
{
std::cout << "Data file not found or unable to open!" << std::endl;
std::system("pause");
exit(1); // exit the program.
}
}
void readIn(std::ifstream &iFile, std::vector<Employee> &employees)
{
std::ios_base::iostate oldstate = iFile.exceptions();
iFile.exceptions(std::ifstream::badbit | std::ifstream::failbit);
try
{
do
{
Employee emp;
iFile >> emp.fName;
iFile >> emp.lName;
std::getline(iFile, emp.title);
iFile >> emp.eID;
iFile >> emp.hoursWorked;
iFile >> emp.wage;
iFile >> emp.deductions;
iFile >> emp.wageType;
iFile >> emp.sPay;
employees.push_back(emp);
}
while (!iFile.eof());
}
catch (const std::ios_base::failure &)
{
std::cout << "Data file corrupted!" << std::endl;
std::system("pause");
exit(1); // exit the program.
}
iFile.exceptions(oldstate);
std::cout << "completed" << std::endl;
}
int main()
{
std::ifstream iFile("data.txt");
check(iFile);
std::vector<Employee> employees;
readIn(iFile, employees);
int sizeOf = employees.size();
for (int a = 0; a < sizeOf; a++)
{
std::cout << "FName " << employees[a].fName
<< " LName " << employees[a].lName
<< " JobTitle " << employees[a].title
<< std::endl;
std::cout << "EmployeeID " << employees[a].eID
<< " Hours Worked " << employees[a].hoursWorked
<< " << employees[a].wageType << " Wage " << employees[a].wage
<< std::endl;
std::cout << "Deductions " << employees[a].deductions
<< " Salary Pay " << employees[a].sPay
<< std::endl;
std::cout << std::endl;
}
std::system("pause");
return 0;
}
Alternatively, since your data is line-based, you should use std::getline() to read each line, and then use std::istringstream to parse values:
void readIn(std::ifstream &iFile, std::vector<Employee> &employees)
{
std::string sTemp;
std::ios_base::iostate oldstate = iFile.exceptions();
iFile.exceptions(std::ifstream::badbit | std::ifstream::failbit);
try
{
do
{
Employee emp;
std::getline(iFile, emp.fName);
std::getline(iFile, emp.lName);
std::getline(iFile, emp.title);
std::getline(iFile, emp.eID);
std::getline(iFile, sTemp);
if (!(std::istringstream(sTemp) >> emp.hoursWorked))
iFile.setstate(std::ifstream::failbit);
std::getline(iFile, sTemp);
if (!(std::istringstream(sTemp) >> emp.wage))
iFile.setstate(std::ifstream::failbit);
std::getline(iFile, sTemp);
if (!(std::istringstream(sTemp) >> emp.deductions))
iFile.setstate(std::ifstream::failbit);
std::getline(iFile, emp.wageType);
std::getline(iFile, sTemp);
if (!(std::istringstream(sTemp) >> emp.sPay))
iFile.setstate(std::ifstream::failbit);
employees.push_back(emp);
}
while (!iFile.eof());
}
catch (const std::ios_base::failure &)
{
std::cout << "Data file corrupted!" << std::endl;
std::system("pause");
exit(1); // exit the program if the file is corrupted.
}
iFile.exceptions(oldstate);
std::cout << "completed" << std::endl;
}
I went derp for a moment. I forgot to read in the hourly or salaried before going to salary pay.

Segmentation fault when accessing a structure

The program works all the way up until it checks for the name the user enters. When you enter the name you wish to search for in the array of structures that have been imported from a file full of customer info) it comes back segmentation fault core dumped. This puzzles me.
#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
using namespace std;
struct AccountsDataBase{
char name[50];
string email;
long int phone;
string address;
};
#define MAX 80
AccountsDataBase * account = new AccountsDataBase[MAX];
void readIn(ifstream& file){
int i=0;
while(!file.eof()){
file >> account[i].name >> account[i].email >> account[i].phone >> account[i].address;
}
}
void getAccount(){
char userPick[50];
char streamName[50];
cout << " What account will we be using? " << endl;
cin.getline(streamName, 50);
for(int i=0; strcmp(account[i].name, streamName)!=0; i++){
if( strcmp(account[i].name, streamName)==0){
cout << "\n\n FOUND IT!! \n\n";
cout << account[i].name << "\n" << account[i].email << "\n" << account[i].phone << "\n" << account[i].address << endl;
}
}
}
int main(){
ifstream file;
file.open("2.dat"); //opens data account records text
readIn(file);
getAccount();
delete account;
return 0;
}
Your loop keeps reading everything into the initial element of the array:
while(!file.eof()){
file >> account[i].name >> account[i].email >> account[i].phone >> account[i].address;
}
because the value of i is never incremented. You can convert this to a for loop, like this:
for (count = 0 ; count < MAX && !file.eof() ; count++) {
file >> account[count].name >> account[count].email >> account[count].phone >> account[count].address;
}
Note that I changed i to count:
AccountsDataBase * account = new AccountsDataBase[MAX];
int count = 0;
This will help you solve another problem - determining when the array ends in the getAccount function. Currently, you assume that the record is always there, so the outer loop keeps going on. Now that you have count, you could change the loop like this:
for(int i=0; i < count && strcmp(account[i].name, streamName)!=0; i++){
if( strcmp(account[i].name, streamName)==0){
cout << "\n\n FOUND IT!! \n\n";
cout << account[i].name << "\n" << account[i].email << "\n" << account[i].phone << "\n" << account[i].address << endl;
break;
}
}
if (i == count) {
cout << "Not found." << endl;
}

Improper Data Reading from File (C++, fstream)

Entire question :
Question 3
You are the owner of a hardware store and need to keep an inventory that can tell you what different tools you have, how many of each you have on hand and the cost of each one. Write a program that initializes the random-access file "hardware.dat" to 100 empty records, let you input the data concerning each tool, enables you to list all your tools, lets you delete a record for a tool that you no longer have and lets you update any information in the file. The tool identification number should be the record number. Use the following information to start your file.
My Code :
int question_3()
{
cout << "Question 3" << endl;
fstream hardware;
hardware.open("hardware.dat" , ios::binary | ios::out);
//Create 100 blank objects---------------------------------------------------------------
if (!hardware)
{
cerr << "File could not be opened." << endl;
exit(1);
}
HardwareData myHardwareData;
for (int counter = 1; counter <= 100; counter++)
{
hardware.write(reinterpret_cast< const char * >(&myHardwareData), sizeof(HardwareData));
}
cout << "Successfully create 100 blank objects and write them into the file." << endl;
hardware.close();
hardware.open("hardware.dat" , ios::binary | ios::out | ios::in);
//Write data-----------------------------------------------------------------------------
int record;
int quantity;
float cost;
string tool_name;
cout << endl;
cout << "Enter record number (1 to 100, 0 to end input) : ";
cin >> record;
while (record != 0)
{
cin.sync();
cout << "Enter tool name : "; getline(cin, tool_name);
cout << "Enter quantity : "; cin >> quantity;
cout << "Enter cost : "; cin >> cost;
myHardwareData.setRecord(record);
myHardwareData.setToolName(tool_name);
myHardwareData.setQuantity(quantity);
myHardwareData.setCost(cost);
hardware.seekp((myHardwareData.getRecord() - 1) * sizeof(HardwareData));
hardware.write(reinterpret_cast<const char *>(&myHardwareData), sizeof(HardwareData));
cout << endl
<< "Enter record number (1 to 100, 0 to end input) : ";
cin >> record;
}
cout << "Successfully write all input data into the file." << endl;
//Read data----------------------------------------------------------------------------
cout << endl;
outputDataLineHead();
hardware.read(reinterpret_cast<char *>(&myHardwareData), sizeof(HardwareData));
int counter = 0;
cout << setprecision(2) << fixed;
while (hardware && !hardware.eof())
{
if (myHardwareData.getRecord() != 0)
outputDataLine(cout, myHardwareData);
hardware.seekp(counter++ * sizeof(HardwareData));
hardware.read(reinterpret_cast<char *>(&myHardwareData), sizeof(HardwareData));
}
return 0;
}
//Function for showing data in line form.-----------------------------------------------
void outputDataLineHead()
{
cout << left << setw(17) << "Record No."
<< left << setw(17) << "Tool Name"
<< left << setw(17) << "Quantity"
<< left << setw(17) << "Cost" << endl;
}
void outputDataLine(ostream &output, const HardwareData &Object_in_file)
{
output << left << setw(17) << Object_in_file.getRecord()
<< left << setw(17) << Object_in_file.getToolName()
<< left << setw(17) << Object_in_file.getQuantity()
<< left << setw(17) << Object_in_file.getCost() << endl;
}
HardwareData.h :
#ifndef HAREWAREDATA_H
#define HAREWAREDATA_H
#include <iostream>
using std::string;
class HardwareData
{
public :
HardwareData(string name = "", int recd = 0, int qutity = 0, float cot = 0.0)
{
setToolName(name);
setRecord(recd);
setQuantity(qutity);
setCost(cot);
}
void setToolName(string name)
{
const char *nameValue = name.data();
int length = 0;
length = (length < 15 ? length : 14);
strncpy(tool_name, nameValue, length);
tool_name[length] = '\n';
}
string getToolName() const
{
return tool_name;
}
void setRecord(int recd)
{
record = recd;
}
int getRecord() const
{
return record;
}
void setQuantity(int qutity)
{
quantity = qutity;
}
int getQuantity() const
{
return quantity;
}
void setCost(float cot)
{
cost = cot;
}
float getCost() const
{
return cost;
}
private :
char tool_name[15];
int record;
int quantity;
float cost;
};
#endif
I want to show the data like the following :
Record No. Tool Name Quantity Cost
4 electric hammer 3 34.32
How to achieve this?
Thank you for your attention.
I think your problem is while reading data.. Please check your variables if they get correct data or not.. You can check this with counting characters or try to printf them.
If they are not correct. You can use such an example which i used in below.
First of all i prefer you to read your line like this example ;
In this example i get coordinates of faces. You should change parameters.. In order not to read no need data
std::string str;
while(std::getline(in, str))
{
sscanf(str.c_str(), "%d %f %f", &fiducial.number, &fiducial.x, &fiducial.y);
coord_Num[fiducial.number] = fiducial.get_number();
coord_X[fiducial.number] = fiducial.get_x();
coord_Y[fiducial.number] = fiducial.get_y();
}
If everything looks fine. You should check
void outputDataLine(ostream &output, const HardwareData &Object_in_file)
The core issue here is that you're reading and writing bytes to/from objects of type HardwareData when rather you should be creating inserters/extractors so you can implement correct I/O semantics. For example:
// Inside HardwareData class
friend std::ostream& operator<<(std::ostream&, const HardwareData&);
friend std::istream& operator>>(std::istream&, HardwareData&);
These two declarations are for the inserter and extractor respectively. Input should consist of extracting into the record, tool_name, quantity and cost data members; and output should simply be an stream insertion which is trivial to implement.
It is often the problem when mixing formatted input with unformatted input that the residual newline inhibits further input. That seems to be the case here:
cin >> record; /*
^^^^^^^^^^^^^^ */
while (record != 0)
{
cin.sync();
cout << "Enter tool name : "; getline(cin, tool_name);
// ^^^^^^^^^^^^^^^^^^^^^^^^
// ...
}
After cin >> record; finishes, there will be a newline left inside the stream. That newline will stop std::getline() from working correctly because std::getline() only reads until the newline.
The fix here is to ignore this new line by using the std::ws manipulator:
std::getline(std::cin >> std::ws, tool_name);
// ^^^^^^^^^^^^^^^^^^^
Note: I talk about this in more detail here.
But this manual extraction isn't needed as we've already defined the inserter and extractor for our class. So all that's really needed is the following:
while (std::cin >> myHardwareData)
{
hardware << myHardwareData;
}
or
std::copy(std::istream_iterator<HardwareData>(std::cin),
std::istream_iterator<HardwareData>(),
std::ostream_iterator<HardwareData>(hardware));
Noticed how I've also taken out the check for a 0 value of record in the while loop. That's because the extractor takes care of it by reflecting a 0 value of record as invalid input. It sets the stream state of the stream if this occurs, thus allowing ourselves to be ejected from the while if that happens:
std::istream& operator>>(std::istream& is, HardwareData& hd)
{
cout << "Enter record number (1 to 100, 0 to end input) : ";
if ((is >> record) && record != 0)
{
// ...
} else
{
is.setstate(std::ios_base::failbit);
}
// ...
}
And the rest of your code be changed to:
std::cout << myHardwareData;
hardware >> myHardwareData;
std::cout << std::setprecision(2) << std::fixed;
while (hardware >> myHardwareData)
{
if (myHardwareData.getRecord() != 0)
std::cout << myHardwareData;
}
I don't really know what the seekps are for. If you elaborate on that, that would really help me adapt my code more accurately to your needs.