C++/fstream... struct... union
Expert: Zlatko - 1/11/2010
QuestionQUESTION: I am doing a program to read a sample infile.txt & ouput to outfile.txt The program is student grade.
I encountered alot of probs which I can't solve now. I am having issues reading from infile.txt & display the correct result in outfile.txt Pls enlighten me further. Thank you.
My code:
[code]#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;
const int MAX = 50;
enum SEX {Male, Female};
struct MaleStudent
{
char name;
int mark1;
int mark2;
int mark3;
};
struct FemaleStudent
{
char name;
int mark1;
int mark2;
int mark3;
int mark4;
};
union Student
{
MaleStudent m;
FemaleStudent f;
};
struct UOWSIM
{
SEX gen;
Student st;
int result;
char grade [MAX];
};
int FileToArray (fstream&, char [], UOWSIM []);
void arrayToOutfile (fstream&, char [], UOWSIM [], int);
int main ()
{
fstream afile;
UOWSIM p [MAX];
UOWSIM stu;
int size = FileToArray (afile, "infile.txt", p);
arrayToOutfile (afile, "outfile.txt", p, size);
}
int FileToArray (fstream& afile, char fileName [], UOWSIM p [])
{
afile.open (fileName, ios::in);
if (!afile.good ())
{
cout << "File opening for writing failed" << endl;
return 1;
}
afile.close ();
}
int findGrade (int marks)
{
if (marks < 50)
{
cout << "Fail";
}
else if (marks <= 64)
{
cout << "Pass";
}
else if (marks <= 74)
{
cout << "Credit";
}
else if (marks <= 84)
{
cout << "Dist";
}
else
{
cout << "HDist";
}
}
void arrayToOutfile (fstream& afile, char fileName [], UOWSIM p [], int size)
{
afile.open (fileName, ios::out);
for (int i = 0; i < size; i++)
{
int result;
int mark1, mark2, mark3, mark4;
result = (mark1 + mark2 + mark3) / 3;
afile << "Student " << i + 1 << endl;
if (p[i].gen == Male)
{
afile << "Name: " << p [i].st.m.name << endl;
afile << "Sex: Male" << endl;
afile << "Assignment 1 " << p [i].st.m.mark1 << endl;
afile << "Assignment 2 " << p [i].st.m.mark2 << endl;
afile << "Assignment 3 " << p [i].st.m.mark3 << endl;
afile << "Result " << p [i].result.m.MaleStudent << endl;// Have a problem with the result in outfile.txt
afile << "Grade " << endl; // Have a problem with the grade display in outfile.txt
}
else
{
afile << "Name: " << p [i].st.f.name << endl;
afile << "Sex: Female" << endl;
afile << "Assignment 1 " << p [i].st.f.mark1 << endl;
afile << "Assignment 2 " << p [i].st.f.mark2 << endl;
afile << "Assignment 3 " << p [i].st.f.mark3 << endl;
afile << "Assignment 4 " << p [i].st.f.mark4 << endl;
afile << "Result " << p [i].result.f.FemaleStudent << endl; // Have a problem with the result in outfile.txt
afile << "Grade " << endl; // Have a problem with the grade display in outfile.txt
}
afile << "------------------------------------------------" << endl;
}
afile.close ();
}
[/code]
sample infile.txt info
[output]
F Mary 70 60 89 77
M Andy 70 89 65
M David 89 88 56
M Michael 70 60 89
F Sally 69 75 66 80
[/output]
Outfile.txt should have the following info:
____________________________________________
Student 1
Name: AAAAAAA
Sex: Male
Assignment 1 67
Assignment 2 88
Assignment 3 78
Result: 78 // Add the 3 assignments / 3
Grade: Dist
------------------------------------------------
Student 2
Name: BBBB
Sex: Female
Assignment 1 89
Assignment 2 70
Assignment 3 60
Assignment 4 77
Result: 74 // Add the 4 assignments / 4
Grade: Credit
------------------------------------------------
ANSWER: Steven. I commend you for doing so much of your assignment before asking for help. I have corrected your compiler errors, and added some comments to your code. My comments start with //XXX. Please look for and read those comments. I've added code to start reading the infile.txt but it is up to you to finish the code. I have made some changes to your arrayToOutfile. I think it is now OK.
It is up to you to decide how much error checking you want when reading the infile.txt
Feel free to ask more questions if you need to.
Here is the code
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <string> //XXX added
#include <sstream> //XXX added
using namespace std;
const int MAX = 50;
enum SEX {Male, Female};
struct MaleStudent
{
int mark1;
int mark2;
int mark3;
};
struct FemaleStudent
{
int mark1;
int mark2;
int mark3;
int mark4;
};
union Student
{
MaleStudent m;
FemaleStudent f;
};
struct UOWSIM
{
SEX gen;
string name; //XXX since both males and females have names, the name can be common. Also it needs to be more than 1 char, make it a string
Student st;
int result;
//XXX removed grade because it is unused. You need to decide if
// you want to store the grades in this array or in mark1/2/3/4
// If you decide to store the grades in the grade array, you will need to
// keep track of how many grades are actually stored in the array. You will need
// a gradeCount variable.
/*
Storing the grades in an array is much better than using mark1/2/3/4.
You will see that when you write code to read the file. Using an array will create more
compact code because you can use a loop with an array index.
*/
//char grade [MAX];
};
//XXX you dont need to pass fstream to these functions because you are passing the filename. Make the fstream local;
int FileToArray (char [], UOWSIM []);
void arrayToOutfile (char [], UOWSIM [], int);
int main ()
{
UOWSIM p [MAX];
//XXX removed becuase it is unused UOWSIM stu;
int size = FileToArray ("infile.txt", p);
arrayToOutfile ("outfile.txt", p, size);
}
int FileToArray (char fileName [], UOWSIM p [])
{
fstream afile;
afile.open (fileName, ios::in);
if (!afile.good ())
{
cout << "File opening for reading failed" << endl;
return 0; //XXX should return 0 if nothing was read
}
//XXX ADDED code to read file. It is not complete. You finish it.
int studentCount = 0;
while(!afile.eof() && studentCount < MAX) //XXX it is very important to check that studentCount<MAX to prevent writing past the end of the array.
{
std::string line;
std::getline(afile, line);
cout << line << endl; //XXX the cout in this loop are for debugging. You should remove them.
stringstream ss(line);
// Parse line into elements
char gender;
ss >> gender;
cout << '\t' << gender << endl;
if (gender == 'F') p[studentCount].gen = Female;
else if (gender = 'M') p[studentCount].gen = Male;
else
{
cout << "File error: Gender ambiguity";
break; //XXX or continue to move to the next record.
}
ss >> p[studentCount].name;
if (gender == 'M')
{
//read 3 marks
string markText;
if (ss.good()) //XXX check ss to see if there is more to read
{
ss >> markText;
p[studentCount].st.m.mark1 = atoi(markText.c_str());
cout << '\t' << p[studentCount].st.m.mark1 << endl;
}
else break; //XXX or continue to move to the next record.
if (ss.good())
{
ss >> markText;
p[studentCount].st.m.mark2 = atoi(markText.c_str());
cout << '\t' << p[studentCount].st.m.mark2 << endl;
}
else break; //XXX or continue to move to the next record.
if (ss.good())
{
ss >> markText;
p[studentCount].st.m.mark3 = atoi(markText.c_str());
cout << '\t' << p[studentCount].st.m.mark3 << endl;
}
else break; //XXX or continue to move to the next record.
}
else
{
//XXX TODO read 4 marks
}
//XXX If no error this is a good record, increment studentCount
++studentCount;
}
afile.close ();
return studentCount;
}
//XXX WAS returning int, should be string or char*
char* findGrade (int marks)
{
if (marks < 50)
{
return "Fail";
}
else if (marks <= 64)
{
return "Pass";
}
else if (marks <= 74)
{
return "Credit";
}
else if (marks <= 84)
{
return "Dist";
}
else
{
return "HDist";
}
}
void arrayToOutfile (char fileName [], UOWSIM p [], int size)
{
fstream afile;
afile.open (fileName, ios::out);
//XXX Added error checking
if (!afile.good ())
{
cout << "File opening for writing failed" << endl;
return; //XXX should return 0 if nothing was read
}
for (int i = 0; i < size; i++)
{
//XXX Removed these lines. Here you want the average of the student's marks
// but male and female students are different and you need to get the marks from the
// student list
//int result;
//int mark1, mark2, mark3, mark4;
//result = (mark1 + mark2 + mark3) / 3;
afile << "Student " << i + 1 << endl;
if (p[i].gen == Male)
{
p[i].result = (p[i].st.m.mark1 + p[i].st.m.mark2 + p[i].st.m.mark3)/3; //XXX added calculation of result for male
afile << "Name: " << p [i].name << endl;
afile << "Sex: Male" << endl;
afile << "Assignment 1 " << p [i].st.m.mark1 << endl;
afile << "Assignment 2 " << p [i].st.m.mark2 << endl;
afile << "Assignment 3 " << p [i].st.m.mark3 << endl;
//XXX WAS afile << "Result " << p [i].result.m.MaleStudent << endl;// Have a problem with the result in outfile.txt
afile << "Result " << p [i].result << endl;// Have a problem with the result in outfile.txt
//XXX added call to findGrade
afile << "Grade " << findGrade(p [i].result) << endl; // Have a problem with the grade display in outfile.txt
}
else
{
p[i].result = (p[i].st.f.mark1 + p[i].st.f.mark2 + p[i].st.f.mark3 +p[i].st.f.mark4)/4; //XXX added calculation of result for female
afile << "Name: " << p [i].name << endl;
afile << "Sex: Female" << endl;
afile << "Assignment 1 " << p [i].st.f.mark1 << endl;
afile << "Assignment 2 " << p [i].st.f.mark2 << endl;
afile << "Assignment 3 " << p [i].st.f.mark3 << endl;
afile << "Assignment 4 " << p [i].st.f.mark4 << endl;
//XXX WAS afile << "Result " << p [i].result.f.FemaleStudent << endl; // Have a problem with the result in outfile.txt
afile << "Result " << p [i].result << endl; // Have a problem with the result in outfile.txt
//XXX added call to findGrade
afile << "Grade " << findGrade(p [i].result) << endl; // Have a problem with the grade display in outfile.txt
}
afile << "------------------------------------------------" << endl;
}
afile.close ();
}
---------- FOLLOW-UP ----------
QUESTION: Hi Zlatko
Tks for the guidance. I have managed to display the assignment results based on my own code & yours. Seriously, I was given those struct format and I was told to follow the rules and not to change it.
I have made changes to my function, findGrade which I was told that this is correct. But how do I call and display in outfile.txt (Line 141)
Btw, I do not need to display anything on the output screen. Everything will be in outfile.txt
Hope to hear from you again.
Here is my amended code:
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MAX = 30;
enum SEX {Male, Female};
struct MaleStudent
{
char name [30];
int mark1;
int mark2;
int mark3;
};
struct FemaleStudent
{
char name [30];
int mark1;
int mark2;
int mark3;
int mark4;
};
union Student
{
MaleStudent m;
FemaleStudent f;
};
struct UOWSIM
{
SEX gen;
Student st;
int result;
char grade [MAX];
};
int FileToArray (fstream&, char [], UOWSIM []);
void arrayToOutfile (fstream&, char [], UOWSIM [], int);
void findGrade (int, char [MAX]);
int main ()
{
fstream afile;
UOWSIM p [MAX];
int size = FileToArray (afile, "infile.txt", p);
arrayToOutfile (afile, "outfile.txt", p, size);
}
//Transfer information from infile.txt to array
int FileToArray (fstream& afile, char fileName [], UOWSIM p [])
{
afile.open (fileName, ios::in);
char n;
int i = 0;
while (afile >> n)
{
switch (n)
{
case 'M': p [i].gen = Male;
afile >> p [i].st.m.name;
afile >> p [i].st.m.mark1;
afile >> p [i].st.m.mark2;
afile >> p [i].st.m.mark3;
break;
case 'F': p [i].gen = Female;
afile >> p [i].st.f.name;
afile >> p [i].st.f.mark1;
afile >> p [i].st.f.mark2;
afile >> p [i].st.f.mark3;
afile >> p [i].st.f.mark4;
}
++i;
}
afile.close ();
return i;
}
//Find the grade computation
void findGrade (int marks, char grade [MAX])
{
if (marks < 50)
{
strcpy (grade, "Fail");
}
else if (marks <= 64)
{
strcpy (grade, "Pass");
}
else if (marks <= 74)
{
strcpy (grade, "Credit");
}
else if (marks <= 84)
{
strcpy (grade, "Dist");
}
else
{
strcpy (grade, "HDist");
}
}
void arrayToOutfile (fstream& afile, char fileName [], UOWSIM p [], int size)
{
afile.open (fileName, ios::out);
for (int i = 0; i < size; i++)
{
int marks;
afile << "Student " << i + 1 << endl;
if (p [i].gen == Male)
{
p [i].result = (p [i].st.m.mark1 + p [i].st.m.mark2 + p [i].st.m.mark3) / 3;
afile << "Name: " << p [i].st.m.name << endl;
afile << "Sex: Male" << endl;
afile << "Assignment 1 " << p [i].st.m.mark1 << endl;
afile << "Assignment 2 " << p [i].st.m.mark2 << endl;
afile << "Assignment 3 " << p [i].st.m.mark3 << endl;
afile << "Result " << p [i].result << endl;
afile << "Grade " << findGrade (marks, char grade [MAX]) << endl; //How do I display the findGrade function to outfile.txt
}
else
{
p [i].result = (p [i].st.f.mark1 + p [i].st.f.mark2 + p [i].st.f.mark3 + p [i].st.f.mark4) / 4;
afile << "Name: " << p [i].st.f.name << endl;
afile << "Sex: Female" << endl;
afile << "Assignment 1 " << p [i].st.f.mark1 << endl;
afile << "Assignment 2 " << p [i].st.f.mark2 << endl;
afile << "Assignment 3 " << p [i].st.f.mark3 << endl;
afile << "Assignment 4 " << p [i].st.f.mark4 << endl;
afile << "Result " << p [i].result << endl;
}
afile << "------------------------------------------------" << endl;
}
afile.close ();
}
AnswerHello Steven.
I like how you did the FileToArray function. Although there is no error checking, you made good use of the fstream >> operator. I should have thought of it.
To answer your question, you need this:
char grade[MAX];
findGrade(p[i].result, grade);
afile << "Grade " << grade << endl;
I don't have access to a compiler now, so I cannot check it, but I believe it is correct. I assume you need one grade based on the average of the student's marks, which is stored in p[i].result.
The other alternative is to return a char* or string from findGrade and call findGrade as I showed you in my last response.
Best regards
Zlatko