I have a program that uses various structs and functions to read information into a struct from an output file, do something with it, and write to an output file if a condition is met. Everything is working properly, except the function that's supposed to write to the output file isn't doing so. I'm required to have a function that writes to the output file, so doing it in main isn't an option.
edit: The function to write to the output file is at the very bottom.
another edit: I only need information written to the output file if the subscription is expired (so if customer.custInfo.monthsLeft==0).
Here's my code:
#include <iostream>
#include <iomanip>
#include <fstream>
#include <climits>
using namespace std;
struct subscriberName{
string firstName;
string lastName;
int custId;
};
struct address{
string street;
string city;
string state;
int zip_code;
};
struct date{
string month;
int day;
int year;
};
struct renewal_information{
int monthsLeft;
date lastNoticeSent;
};
struct subscriber{
subscriberName custName;
address custAddress;
renewal_information custInfo;
};
void openInFile(ifstream&);
void openOutFile(ofstream&);
subscriber recordIn(ifstream&, subscriber&, address&, date&, int&, int&);
void expired(subscriber&, ofstream&);
int main() {
ifstream inFile;
ofstream outFile;
openInFile(inFile);
openOutFile(outFile);
subscriber customer;
address custAddress;
date custDate;
int currentLine=0, numProcessed=0, numExpired=0;
while (!inFile.eof()){
recordIn(inFile, customer, custAddress, custDate, currentLine, numProcessed);
if (customer.custInfo.monthsLeft==0) {
expired(customer, outFile);
numExpired++;
}
}
cout<<endl<<string(47, '-')<<endl<<"Number of subscribers processed: "<<numProcessed
<<endl<<"The number of expired subscriptions is: " <<numExpired<<endl
<<string(47, '-')<<endl<<endl;
inFile.close();
outFile.close();
return 0;
}
void openInFile(ifstream& inFile){
string inFileName;
do{
cout<<"Enter input file name: ";
cin>>inFileName;
cout<<inFileName<<endl;
inFile.open(inFileName.c_str());
}
while (!inFile);
if (inFile.fail()){
cout<<"input file failed to open\n";
inFile.clear();
} else
cout<<inFileName<<" opened successfully\n";
}
void openOutFile(ofstream&){
string outFileName;
ofstream outFile;
do{
cout<<"Enter output file name: ";
cin>>outFileName;
cout<<outFileName<<endl;
outFile.open(outFileName.c_str());
}
while (!outFile);
if (outFile.fail()){
cout<<"output file failed to open\n";
outFile.clear();
} else
cout<<outFileName<<" opened successfully\n";
}
subscriber recordIn(ifstream& inFile, subscriber& customer, address& custAddress, date& custDate, int& currentLine, int& numProcessed){
inFile.ignore(currentLine, '\n');
getline(inFile, customer.custName.firstName, '\n');
if (inFile.eof()){
return customer;
}
else {
getline(inFile, customer.custName.lastName, '\n');
inFile >> customer.custName.custId;
cout << "==> Processing Customer ID: " << customer.custName.custId << endl;
numProcessed++;
inFile.ignore(INT_MAX, '\n');
getline(inFile, customer.custAddress.street, '\n');
getline(inFile, customer.custAddress.city, '\n');
getline(inFile, customer.custAddress.state, '\n');
inFile >> customer.custAddress.zip_code;
inFile >> customer.custInfo.monthsLeft;
inFile >> customer.custInfo.lastNoticeSent.month;
inFile >> customer.custInfo.lastNoticeSent.day;
inFile >> customer.custInfo.lastNoticeSent.year;
currentLine = currentLine + 11;
}
return customer;
}
void expired(subscriber& customer, ofstream& outFile){
while (customer.custInfo.monthsLeft==0) {
outFile << customer.custName.firstName;
outFile << customer.custName.lastName;
outFile << customer.custName.custId;
outFile << customer.custAddress.street;
outFile << customer.custAddress.city;
outFile << customer.custAddress.state;
outFile << customer.custAddress.zip_code;
outFile << customer.custInfo.monthsLeft;
outFile << customer.custInfo.lastNoticeSent.month;
outFile << customer.custInfo.lastNoticeSent.day;
outFile << customer.custInfo.lastNoticeSent.year;
customer.custInfo.monthsLeft=-1;
outFile.flush();
}
}
In this code
void openOutFile(ofstream&){
string outFileName;
ofstream outFile;
...
}
outFile should be the parameter of openOutFile and not a local variable, otherwise the call to openOutFile(outFile); does not return with an open stream outFile.
Related
I'm trying to take the profile info(username, email, etc.) from one directory and put it in another. I've been debugging the code for this program, and while there are no errors, the program won't run, saying that the program "has stopped working". I have already looked on this website and others for any possible answers, and found none.
#include <string>
#include <cstring>
#include <iostream>
#include <istream>
#include <ostream>
#include <fstream>
#include <iomanip>
#include <filesystem>
using namespace std;
class path{
public:
string parent_directory;
string root_directory;
};
class Data{
public:
string userName;
string nickName;
string fName;
string arena_FName;
string lName;
string arena_LName;
string email;
string arenaEmail;
friend std::istream& operator>>(std::istream& input, Data& d);
};
std::istream& operator>>(std::istream& input, Data& d){
std::getline(input, d.userName);
std::getline(input, d.nickName);
//...
std::getline(input, d.arenaEmail);
return input;
}
int main(){
ifstream myfile("myfunk.txt", ios::in);
ofstream arena("arena.txt");
myfile.open("myfunk.txt", ios::in);
if(myfile){
cout << "Input file open." << endl;
}
arena.open("arena.txt", ios::out | ios::app);
if(arena){
cout << "Output file open." << endl;
}
cout << "file opening test: success" << endl;
int x = 0;
int y = 4101; //Total number of users in the directory.
int z = 0; //For inputting the required lines of info for each profile.
int profile = 0;
bool valid = false;
string role;
//string arenaRole;
bool post = false;
string line;
string p = "This PC/..."; //Path to the folder of the individual pictures.
//myVar.save("...");
string p = "...";
path path1;
path root_directory;
path parent_directory;
//bool is_directory(const std::filesystem::path& p, std::error_code& ec) noexcept; //Checks if current location is a directory.
//bool postPic;
const unsigned int MAXIMUM_DATA = 4100u;
Data database[MAXIMUM_DATA];
cout << "All variables but the filesystem have been accepted! Please install this program on the network." << endl;
while(x < y){
cout << "Primary loop functioning" << endl;
if(post = true){
getline(myfile, line); //Grab and read next line.
myfile >> line;
line = userName[x];
arena << "Username: " << userName[x] << "\n";
z++;
getline(myfile, line);
myfile >> line;
line = role[x];
arena << "Role: " << role[x] << "\n";
z++;
getline(myfile, line);
line = nickName[x];
myfile >> nickName[x];
arena << "nickname: " << nickName[x] << "\n";
z++;
getline(myfile, line);
line = fName[x];
myfile >> fName;
arena << "First Name: " << fName[x] << "\n";
z++;
getline(myfile, line);
line = lName[x];
myfile >> lName;
arena << "Last Name: " << lName[x] << "\n";
z++;
getline(myfile, line);
myfile >> line;
line = email[x];
arena << "Email: " << email[x] << "\n";
getline(myfile, line);
z = 0; //Next profile...
}
int data;
while(myfile >> data){
if(nickName[x] = NULL){
myfile >> "<Error> Some required information is missing! Contact user! </Error> /n";
valid = false;
post = false;
x++;
}
if(email[x] != NULL){
std::string str("#");
std::string str2(".com");
std::string str3(".net");
std::string str4(".edu");
if(std::size_t found = email[x].find(str) & (std::size_t found = email[x].find(str2) || std::size_t found = email[x].find(str3) || std::size_t found = email[x].find(str4)){
valid = true;
if(valid = true){
post = true;
}
}
else{
valid = false;
post = false;
x++;
}
}
}
}
}
}
x++;
}
//x++;
myfile.close(); //Closes the file in the directory.
arena.close(); //Closes the file in Arena.
return 0;
}
Let's rework your code.
First, let's create a data structure for the data:
class Data
{
public:
string userName;
string nickName;
string fName;
string arena_FName;
string lName;
string arena_LName;
string email;
string arenaEmail;
};
If you need an array for the data, it would be declared as:
const unsigned int MAXIMUM_DATA = 4100u;
Data database[MAXIMUM_DATA];
Next, let's overload the extraction operator>> to make reading easier:
class Data
{
public:
//...
friend std::istream& operator>>(std::istream& input, Data& d);
};
std::istream& operator>>(std::istream& input, Data& d)
{
std::getline(input, d.userName);
std::getline(input, d.nickName);
//...
std::getline(input, d.arenaEmail);
return input;
}
This simplifies your input loop to:
std::vector<Data> database;
Data d;
while (my_file >> d)
{
database.push_back(d);
}
You can query the amount of data read in by using the std::vector::size() method, i.e. database.size().
Also, you don't need a separate structure for a file path. A simple std::string will suffice. I recommend using forward slash, '/', because it is recognized by both Windows and *nix operating systems and won't be interpreted as an escape character.
Here is my code for an assignment I have. Whenever I try and compile I get an error for my read function due to something in "ios_base.h" I am not sure what to do and/or if my code does the intended function of taking a file and moving it's elements into a separate file that has the name and average next to each other.
#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
using namespace std;
struct Student
{
string fname;
string lname;
double average;
};
int read(ifstream, Student s[]);
void print(ofstream fout, Student s[], int amount);
int main()
{
const int size = 10;
ifstream fin;
ofstream fout;
string inputFile;
string outputFile;
Student s[size];
cout << "Enter input filename: ";
cin >> inputFile;
cout << "Enter output filename: ";
cin >> outputFile;
cout << endl;
fin.open(inputFile.c_str());
fout.open(outputFile.c_str());
read(fin , s);
print(fout, s, read(fin, s));
}
int read(ifstream fin, Student s[])
{
string line;
string firstName;
string lastName;
double score;
double total;
int i=0;
int totalStudents=0;
Student stu;
while(getline(fin, line)){
istringstream sin;
sin.str(line);
while(sin >> firstName >> lastName){
stu.fname = firstName;
stu.lname = lastName;
while(sin >> score){
total *= score;
i++;
}
stu.average = (total/i);
}
s[totalStudents]=stu;
totalStudents++;
}
return totalStudents;
}
void print(ofstream fout, Student s[], int amount)
{
ostringstream sout;
for(int i = 0; i<amount; i++)
{
sout << left << setw(20) << s[i].lname << ", " << s[i].fname;
fout << sout << setprecision(2) << fixed << "= " << s[i].average;
}
}
Stream objects are not copyable. Their copy constructor is deleted. They must be passed by reference, not by value:
int read(ifstream &, Student s[]);
void print(ofstream &fout, Student s[], int amount);
etc...
Sam Varshavchik's answer is correct, but he didn't mention why stream objects don't allow you to copy them.
The issue here is that a stream object owns a buffer, and buffers can't be copied safely.
To take an example, suppose you have data coming in over a network socket and a buffer sitting in front of it, and you copy this buffered reader. If you read from the copy, it will read some indeterminate amount of data and put it into the buffer. This data is now gone from the network socket and only exists in the buffer. Now suppose you read from the copy. Then you'll get some indeterminate amount of data that came after the data you read in the original. Going back and forth in this way, you'd get two "streams" with gaps in them where the other reader was reading the data.
My .txt file contains the following
12345
John Smith
45 12.50
54234
Joe Paul
32 10.25
12324
Chris Lee
43 22.50
The first int is the employee ID, then the string name, then hours and rate of pay(float).
My Program:
using namespace std;
const int Size = 10;
typedef int intarray[Size];
typedef float farray[Size];
typedef string starray[Size];
void readdata(intarray,starray,intarray,farray, ifstream &);
void print(intarray,starray,intarray,farray,ifstream &);
int main()
{
ifstream fin("data.txt");
//ofstream fout;
starray EmployeeName;
intarray EmployeeID,EmployeeHoursWorked;
farray EmployeeRate;
readdata(EmployeeID,EmployeeName,EmployeeHoursWorked,EmployeeRate,fin);
print(EmployeeID,EmployeeName,EmployeeHoursWorked,EmployeeRate,fin);
cin.get();
cin.ignore();
}
My functions, my code is not properly reading the file.
void readdata(intarray ID, starray Name, intarray Hours, farray Rate, ifstream & infile)
{
for(int i = 0; i < Size; i++)
{
//this code is not working as intended
infile >> ID[i];
getline(infile, Name[i]);
infile >> Hours[i] >> Rate[i];
infile.ignore();
}
}
Im not getting any results.
void print(intarray ID, starray Name, intarray Hours, farray Rate, ifstream & infile)
{
for(int i = 0; i < Size; i++)
{
cout << ID[i] << endl;
cout<<Name[i]<<endl;
cout << Hours[i] << Rate[i] << endl;
}
}
When you call getline in readdata you are reading the end of line character after the ID. You need to be positioned on the line with the name prior to calling getline. Try this
infile >> ID[i];
infile.ignore(); //Consume the new line char
getline(infile, Name[i]);
infile >> Hours[i] >> Rate[i];
#include <iostream>
#include <string>
#include <fstream>
using namespace std;
void read();
int main() {
read();
return 0;
}
void read () {
string file("");
string nameOfFile("");
cin >> nameOfFile;
ifstream in (nameOfFile);
while ( !in.eof() ) {
getline(in, file);
cout << file;
cout << endl;
}
cout << file;
in.close();
}
How come this isn't working, I'm trying to make it so i can type in which file i want to read?
I'm really new to C++, sorry if this is an obvious fix.
You have to change
ifstream in (nameOfFile);
with
ifstream in (nameOfFile.c_str());
because the default constructor for ifstream does not accept a std::string as an argument, it needs a char *. Hence, use the function std::string::c_str() to convert a std::string into a char *.
A little feedback:
void read () {
string file(""); // you don't need the ("") bit; empty by default,
// and "file" is a terrible choice of identifier as
// it sounds more like an ifstream than a string
// used to hold one line from the file.
// I tend to use "string line;" for this.
string nameOfFile(""); // ditto
cin >> nameOfFile; // you should test for success of input, like this:
// if (!cin >> nameOfFile) {
// std::cerr << "error reading filename from stdin\n";
// exit(1);
// }
ifstream in (nameOfFile); // test for success getting file open like this:
// if (ifstream in(nameofFile))
// {
while ( !in.eof() ) { // NEVER check eof before attempting input, instead:
getline(in, file); // while (getline(in, file))
cout << file; // cout << file << endl; // can "chain"
cout << endl; // }
// else
// std::cerr << "couldn't open " << nameOfFile
// << '\n';
} // no need for extra cout nor explicit close, as
cout << file; // the ifstream destructor closes anyway.
in.close();
}
You need to open the ifstream usign in.open(), and hendle the case where file does not exist as well. here is the function:
void read() {
string file("");
string fileContent = "";
string nameOfFile("");
cin >> nameOfFile;
ifstream in(nameOfFile.c_str());
in.open(nameOfFile, ios::in);
if (in){
while (!in.eof()) {
getline(in, file);
fileContent += file;
}
cout << fileContent;
in.close();
}
else {
cout << "Could not open file.";
}
}
I have posted the following code where I am reading from an input file -- storing information in a structure -- and then writing to an output file. I know that the eof function is not safe and hence one must use the getline function to check whether the end of file has been detected or not; however, in this particular code, I have not been able to use the getline function and hence has finally relied on the eof function. Hence, can you please suggest an alternative to the eof function or let me know how I can use the getline function when I am trying to initialize an array of structures . I have used two asterisk symbols to indicate where I want to use the getline function.
#include <iostream>
#include <fstream>
using namespace std;
//student structure
struct student
{
char name[30];
char course[15];
int age;
float GPA;
};
ifstream inFile;
ofstream outFile;
student getData();
void writeData(student writeStudent);
void openFile();
int main (void)
{
const int noOfStudents = 3; // Total no of students
openFile(); // opening input and output files
student students[noOfStudents]; // array of students
// Reading the data from the file and populating the array
for(int i = 0; i < noOfStudents; i++)
{
if (!inFile.eof()) // ** This where I am trying to use a getline function.
students[i] = getData();
else
break ;
}
for(int i = 0; i < noOfStudents; i++)
writeData(students[i]);
// Closing the input and output files
inFile.close ( ) ;
outFile.close ( ) ;
}
void openFile()
{
inFile.open("input.txt", ios::in);
inFile.seekg(0L, ios::beg);
outFile.open("output.txt", ios::out | ios::app);
outFile.seekp(0L, ios::end);
if(!inFile || !outFile)
{
cout << "Error in opening the file" << endl;
exit(1);
}
}
student getData()
{
student tempStudent;
// temp variables for reading the data from file
char tempAge[2];
char tempGPA[5];
// Reading a line from the file and assigning to the variables
inFile.getline(tempStudent.name, '\n');
inFile.getline(tempStudent.course, '\n');
inFile.getline(tempAge, '\n');
tempStudent.age = atoi(tempAge);
inFile.getline(tempGPA, '\n');
tempStudent.GPA = atof(tempGPA);
// Returning the tempStudent structure
return tempStudent;
}
void writeData(student writeStudent)
{
outFile << writeStudent.name << endl;
outFile << writeStudent.course << endl;
outFile << writeStudent.age << endl;
outFile << writeStudent.GPA << endl;
}
You want to write an operator>> for your student type. Something like:
std::istream& operator>>(std::istream& in, student& s) {
in >> s.age; // etc.
return in;
}
Which then allows you to write:
int studentNo = 0;
students[maxStudents];
while (studentNo < maxStudents && (in >> students[studentNo]))
++studentNo;
Why not write this way?
instead of
inFile.getline(tempStudent.name, '\n');
inFile.getline(tempStudent.course, '\n');
inFile.getline(tempAge, '\n');
You may
while(inFile.getline(tempStudent.name, '\n'))
{
inFile.getline(tempStudent.course, '\n');
inFile.getline(tempAge, '\n');
//do stuffs
}