Reading from file using a struct with an enum


I am supposed to read a table of content with members in the struct which includes an enum. There's a read function to be made. Also I'm supposed to output the content but using vector of structs, one for reading and one in the main which can then be used by other functions such as printing the content. In the file, gender is given as 1 for female and 0 for male. I'm supposed output "male" or "female" when printing. But I am really struggling to understand enum. There seems to be no errors using my compiler but there's no output.


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
 #include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <fstream>
using namespace std;

struct Student
{
    string fname, lname, id, cname;
    double Icount;
    enum gender { Male = 0, Female = 1 };
    gender g;
};
vector < Student > readStudents(string input)
{
    vector < Student > st;
    string temp;
    ifstream f;
    f.open("input.txt");
    if (f.fail())
        cout << "Error opening file";
    exit(1);
    f >> input;
    stringstream numb(input);
    int num = 0;
    numb >> num;
    while (getline(f, temp) && (temp.find('\n'))){};
    for (int i = 0; i < num; i++) {
        int g;
        f >> st[i].id >> g; st[i].g = Student::gender(g); 
        f >> st[i].fname >> st[i].lname >> st[i].cname >> st[i].Icount;
    }
    f.close();
}

void printStudent(const Student& st)
{
    cout << "ID [" << st.id << "] First Name [" << st.fname << "] Last Name [" << st.
        lname << "] Gender [" << st.g << "] Course [" << st.
        cname << "] Attendance [" << st.Icount << "]\n";
}

vector < Student > getFemales(const vector < Student >& stds)
{
    cout << "The Female Students are:\n";
    int i = 0;
    string s;
    while (stds[i].g == 1)
    {
        cout << "ID [" << stds[i].
            id << "] First Name [" << stds[i].fname << "] Last Name [" << stds[i].
            lname << "] Gender [" << stds[i].g << "] Course [" << stds[i].
            cname << "] Attendance [" << stds[i].Icount << "]\n";
        i++;
    }
    return stds;
}

vector < Student > getLowAttendance(const vector < Student >& stds)
{
    cout << "The Students with Low Attendance are: \n";
    int i = 0;
    while (stds[i].Icount < 20)
    {
        cout << "ID [" << stds[i].
            id << "] First Name [" << stds[i].fname << "] Last Name [" << stds[i].
            lname << "] Gender [" << stds[i].g << "] Course [" << stds[i].
            cname << "] Attendance [" << stds[i].Icount << "]\n";
        i++;
    }
    return stds;
}

int main()
{
    int num = 0;
    string input;
    vector < Student > stds_vect = readStudents(input);
    stringstream numb(input);
    numb >> num;
    for (int i = 0; i < num; i++)
        printStudent(stds_vect[i]);
    getFemales(stds_vect);
    getLowAttendance(stds_vect);
}
Look at L23. I don't think you mean to exit the program if the file open is OK...

PS. It's better to have return 1 rather than exit(1) in main()
Last edited on
Always put braces around statements inside an if or loop,even if there is only 1 statement, it helps prevent this kind of error.
You need to return st in readStudents(...)

Line 31: Before you can use st like so you need to resize it:
1
2
3
4
5
6
7
8
9
    st.resize(num); // Note
    for (int i = 0; i < num; i++) {
        int g;
        f >> st[i].id >> g; st[i].g = Student::gender(g); 
        f >> st[i].fname >> st[i].lname >> st[i].cname >> st[i].Icount;
    }
    f.close();

    return st; // Note 
Thank you for the suggestion. I'm finally getting an output but it looks like this:
The Female Students are:
The Students with Low Attendance are:
ID [] First Name [] Last Name [] Gender [-858993460] Course [] Attendance [0]
ID [] First Name [] Last Name [] Gender [-858993460] Course [] Attendance [0]
ID [] First Name [] Last Name [] Gender [-858993460] Course [] Attendance [0]
ID [] First Name [] Last Name [] Gender [-858993460] Course [] Attendance [0]
ID [] First Name [] Last Name [] Gender [-858993460] Course [] Attendance [0]
ID [] First Name [] Last Name [] Gender [-858993460] Course [] Attendance [0]
ID [] First Name [] Last Name [] Gender [-858993460] Course [] Attendance [0]
ID [] First Name [] Last Name [] Gender [-858993460] Course [] Attendance [0]
Last edited on
Post your file data (or a sample if it's a lot).
What is line 28 for? It skips all available lines and leaves the in an error state. The following loop reads nothing/does not alter the variables.

1
2
3
4
5
6
7
8
9
10
11
12
int main()
{
    int num = 0;
    string input;
    vector < Student > stds_vect = readStudents(input);
    stringstream numb(input);
    numb >> num;
    for (int i = 0; i < num stds_vect.size(); i++)
        printStudent(stds_vect[i]);
    getFemales(stds_vect);
    getLowAttendance(stds_vect);
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
vector < Student > void getFemales(const vector < Student >& stds)
{
    cout << "The Female Students are:\n";
    int i = 0;
    string s;
    for (int i = 0; i < stds.size(); i++)
{
    if(stds[i].g == 1)
    {
        printStudent(stds[i]);
        cout << "ID [" << stds[i].
           id << "] First Name [" << stds[i].fname << "] Last Name [" << stds[i].
            lname << "] Gender [" << stds[i].g << "] Course [" << stds[i].
            cname << "] Attendance [" << stds[i].Icount << "]\n";
        i++;
    }
}
    return stds;
}
Similar for getLowAttendance(...)
I am so sorry for responding late, my day's been very hectic. First, line 28 is to ignore the first line of headers on the file (pasted below). We were told not to use ignore and instead use getline so I tried this. Second, for both getFemales and getLowAttendance, we're supposed to use vector <Student> , I can't change it. I never learnt it before so I tried my best to figure it out.

File contents:
8
ID Gender (enum 0:m, 1:f) First Name LName Course Attendance
1 0 Mohammad Saleh CMP220 30
2 1 Sara Ali CMP220 28
5 0 Charles Dickens CMP220 25
4 1 Jain Austin CMP220 23
19 1 Ahlam Mosteghanemi CMP220 15
21 0 Mario Puzo CMP220 21
88 1 Marie Curie CMP220 30
1930 0 John Steinbeck CMP220 19
Also how would the code change if I used enum class instead of enum?
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
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <sstream>
#include <format>

struct Student
{
   std::string id;

   enum gender { Male = 0, Female = 1 };

   gender      g { };
   std::string fname;
   std::string lname;
   std::string cname;
   double      Icount { };
};

int main()
{
   std::ifstream myfile("studentdata.txt", std::ios::in);

   std::string line;

   std::vector<Student> students;

   if (!myfile.is_open())
   {
      std::cerr << "** ERROR OPENING FILE! **\n";
      return 1; // terminate, can't continue
   }
   else
   {
      // read the number of data lines
      std::getline(myfile, line);
      int num_lines { std::stoi(line) };

      // read the header line and toss
      std::getline(myfile, line);

      for (int i { }; i < num_lines; ++i)
      {
         std::getline(myfile, line);

         Student student;

         std::istringstream data(line);

         data >> student.id;

         int temp_g;
         data >> temp_g;
         student.g = Student::gender(temp_g);

         data >> student.fname;
         data >> student.lname;
         data >> student.cname;
         data >> student.Icount;

         students.push_back(student);
      }
      myfile.close();
   }

   std::cout << "There are " << students.size() << " students.\n\n";

   const auto format { "{:>5}{:>8}{:>15}{:>15}{:>10}{:>12}\n" };

   std::cout << std::format(format, "ID", "Gender", "First Name", "Last Name", "Course", "Attendance");

   for (const auto& [id, gender, first, last, course, attendance] : students)
   {
      std::string temp_gender;

      std::cout << std::format(format, 
                               id, (gender != Student::Male ? "Female" : "Male"),
                               first, last, course, attendance);
   }
}
There are 8 students.

   ID  Gender     First Name      Last Name    Course  Attendance
    1    Male       Mohammad          Saleh    CMP220          30
    2  Female           Sara            Ali    CMP220          28
    5    Male        Charles        Dickens    CMP220          25
    4  Female           Jain         Austin    CMP220          23
   19  Female          Ahlam   Mosteghanemi    CMP220          15
   21    Male          Mario           Puzo    CMP220          21
   88  Female          Marie          Curie    CMP220          30
 1930    Male           John      Steinbeck    CMP220          19
I'm terribly sorry but that file is only for input. I don't mean to output that. I'm supposed to output it in a line format for each student with header like ID [part. student's ID] First Name [name of student]... not to output what's already in the file.

After outputting all students' info like that. I then proceed to find first the female and then those with an attendance below 20 in the exact same format.
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
#include <iostream>
#include <iomanip>
#include <fstream>
#include <string>
#include <vector>
using namespace std;

struct Student
{
   string id;
   int gender;
   string fname, lname;
   string course;
   int attendance;

   friend istream& operator >> ( istream &in, Student &s )
   {
      return in >> s.id >> s.gender >> s.fname >> s.lname >> s.course >> s.attendance;
   }

   friend ostream& operator << ( ostream &out, const Student &s )
   {
      const string MF[] = { "male", "female" };
      return out << setw(5) << s.id 
                 << setw(7) << MF[s.gender]
                 << setw(15) << s.fname << setw(15) << s.lname
                 << setw(8) << s.course 
                 << setw(3) << s.attendance;
   }
};


void print( const vector<Student> &cohort, bool f( Student ) = []( Student s ){ return true; } )
{
   for ( Student s : cohort ) if ( f( s ) ) cout << s << '\n';
}


int main()
{
   vector<Student> cohort;
   ifstream in( "input.txt" );
   string header;   for ( int h = 0; h < 2; h++ ) getline( in, header );
   for ( Student s; in >> s; cohort.emplace_back( s ) );

   cout << "All students:\n";
   print( cohort );

   cout << "\n\nAll females:\n";
   print( cohort, []( Student s ){ return s.gender == 1; } );

   cout << "\n\nLow attendance:\n";
   print( cohort, []( Student s ){ return s.attendance < 20; } );
}


All students:
    1   male       Mohammad          Saleh  CMP220 30
    2 female           Sara            Ali  CMP220 28
    5   male        Charles        Dickens  CMP220 25
    4 female           Jain         Austin  CMP220 23
   19 female          Ahlam   Mosteghanemi  CMP220 15
   21   male          Mario           Puzo  CMP220 21
   88 female          Marie          Curie  CMP220 30
 1930   male           John      Steinbeck  CMP220 19


All females:
    2 female           Sara            Ali  CMP220 28
    4 female           Jain         Austin  CMP220 23
   19 female          Ahlam   Mosteghanemi  CMP220 15
   88 female          Marie          Curie  CMP220 30


Low attendance:
   19 female          Ahlam   Mosteghanemi  CMP220 15
 1930   male           John      Steinbeck  CMP220 19

Can you guys try using the original functions? I honestly still don't know how vector<student> in a function even works. Sorry for any inconvenience caused. I really want to put my work into this.
Your readStudents() function has several problems. First you never use the function parameter, so it isn't needed. Next the exit() function appears to be outside any control statement, it should be inside the if() statement. And by the way you should avoid using exit() in a C++ program.

Next since the number of records is on it's own line you will need to retrieve the end of line character before you use getline().

EDIT: Also you should initializing the vector with the number of records, otherwise you will need separate variables to retrieve the values, then push_back those values into the vector.
END EDIT

You also need to return a value from the function.
Last edited on
@hshdhjsj12345. Based upon your code then perhaps:

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
#include <iostream>
#include <string>
#include <vector>
#include <fstream>
#include <iterator>

enum gender { Male = 0, Female = 1 };

struct Student {
	std::string fname, lname, id, cname;
	double Icount {};
	int g {};
};

auto readStudents(const std::string& filnam) {
	std::vector<Student> studs;

	if (std::fstream f {filnam}) {
		std::string temp;

		std::getline(f >> temp >> std::ws, temp);
		for (Student st; f >> st.id >> st.g >> st.fname >> st.lname >> st.cname >> st.Icount; studs.emplace_back(std::move(st)))studs;
	}

	return studs;
}

void printStudent(const Student& st) {
	constexpr const char* gen[2] {"male", "female"};

	std::cout << "ID [" << st.id << "] First Name [" << st.fname << "] Last Name [" << st.
		lname << "] Gender [" << gen[st.g] << "] Course [" << st.
		cname << "] Attendance [" << st.Icount << "]\n";
}

void getFemales(const std::vector<Student>& stds) {
	std::cout << "\nThe Female Students are:\n\n";

	for (const auto& st : stds)
		if (st.g == Female)
			printStudent(st);
}

void getLowAttendance(const std::vector<Student>& stds) {
	std::cout << "\nThe Students with Low Attendance are: \n\n";

	for (const auto& st : stds)
		if (st.Icount < 20)
			printStudent(st);
}

int main() {
	const auto stds_vect {readStudents("input.txt")};

	if (stds_vect.empty())
		return (std::cout << "Error opening file/No data\n"), 1;

	for (const auto& s : stds_vect)
		printStudent(s);

	getFemales(stds_vect);
	getLowAttendance(stds_vect);
}




ID [1] First Name [Mohammad] Last Name [Saleh] Gender [male] Course [CMP220] Attendance [30]
ID [2] First Name [Sara] Last Name [Ali] Gender [female] Course [CMP220] Attendance [28]
ID [5] First Name [Charles] Last Name [Dickens] Gender [male] Course [CMP220] Attendance [25]
ID [4] First Name [Jain] Last Name [Austin] Gender [female] Course [CMP220] Attendance [23]
ID [19] First Name [Ahlam] Last Name [Mosteghanemi] Gender [female] Course [CMP220] Attendance [15]
ID [21] First Name [Mario] Last Name [Puzo] Gender [male] Course [CMP220] Attendance [21]
ID [88] First Name [Marie] Last Name [Curie] Gender [female] Course [CMP220] Attendance [30]
ID [1930] First Name [John] Last Name [Steinbeck] Gender [male] Course [CMP220] Attendance [19]

The Female Students are:

ID [2] First Name [Sara] Last Name [Ali] Gender [female] Course [CMP220] Attendance [28]
ID [4] First Name [Jain] Last Name [Austin] Gender [female] Course [CMP220] Attendance [23]
ID [19] First Name [Ahlam] Last Name [Mosteghanemi] Gender [female] Course [CMP220] Attendance [15]
ID [88] First Name [Marie] Last Name [Curie] Gender [female] Course [CMP220] Attendance [30]

The Students with Low Attendance are:

ID [19] First Name [Ahlam] Last Name [Mosteghanemi] Gender [female] Course [CMP220] Attendance [15]
ID [1930] First Name [John] Last Name [Steinbeck] Gender [male] Course [CMP220] Attendance [19]

Topic archived. No new replies allowed.