How to read a text file of varying line size into a data structure?

Hiya. I am attempting to read some data from a text file into an array. I noticed that I can not do it the traditional way as the names are of different lengths and not all the students have all their marks. I want to be able to read the data but I am unsure as to whether it is possible to do so. Any help would be much appreciated.

1
2
3
4
5
6
7
8
    struct SStudent
    {
        int number;
        string name;
        int mark1 = 0;
        int mark2 = 0;
        int mark3 = 0;
    }


Students marks file:

1 John Higgins
2 Jack Michael Cullen 12 13 15
3 James Morley 41 56 76
Last edited on
Here's one possibility:

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

struct SStudent
{
    int number {};
    string name;
    int mark[3] {-1,-1,-1}; // -1 means no mark
};

ostream& operator<<(ostream& out, const SStudent& s)
{
    return out << s.number << '\n'
               << s.name   << '\n'
               << s.mark[0] << ' ' << s.mark[1] << ' ' << s.mark[2] << '\n';
}

int main()
{
    // For testing (in actual program: ifstream in("filename"))
    istringstream in
    {
        "1 John Higgins\n"
        "2 Jack Michael Cullen 12 13 15\n"
        "3 James Morley 41 56 76\n"
    };

    vector<SStudent> studs;

    for (string line; getline(in, line); )
    {
        istringstream sin(line);
        SStudent s;
        sin >> s.number >> ws; // ws skips whitespace
        
        // Read chars until end-of-line or a digit.
        for (char ch; sin.get(ch) && !isdigit(ch); ) s.name += ch;

        if (sin) sin.unget(); // if it read a digit, put it back

        // Remove extra space(s) from end of name
        while (!s.name.empty() && isspace(s.name.back())) s.name.pop_back();

        sin >> s.mark[0] >> s.mark[1] >> s.mark[2];
        studs.push_back(s);
    }

    for (const auto& s: studs) cout << s << '\n';
}

Last edited on
I must say your method is pretty ingenious. Forgot all about stringstream. May need a refresher course. xxoxx
An alternative take is to use a vector<int> for the marks so that any number of marks can be present and assume that name can be absent and try to read marks and treat as a name if extraction isn't a number. Consider:

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

struct SStudent
{
	int number {};
	std::string name;
	std::vector<int> marks;
};

std::ostream& operator<<(std::ostream& out, const SStudent& s)
{
	out << s.number << '\n' << s.name << '\n';

	for (const auto& m : s.marks)
		out << m << ' ';

	return out;
}

std::istream& operator>>(std::istream& in, SStudent& s)
{
	s.name.clear();
	s.marks.clear();

	std::string line;

	if (std::getline(in, line)) {
		std::istringstream iss (line);

		if (iss >> s.number)
			while (iss) {
				for (int m; iss >> m; s.marks.push_back(m));

				if (!iss.eof()) {
					std::string na;

					iss.clear();
					iss >> na;

					if (!s.name.empty())
						s.name += ' ';

					s.name += na;
				}
			}
	}

	return in;
}

int main()
{
	// For testing (in actual program: ifstream in("filename"))
	std::istringstream in
	{
		"1 John Higgins\n"
		"2 Jack Michael Cullen 12 13 15\n"
		"3 James Morley 41 56 76\n"
	};

	std::vector<SStudent> studs;

	for (SStudent s; in >> s; studs.push_back(s));

	for (const auto& s : studs)
		std::cout << s << "\n\n";
}



1
John Higgins


2
Jack Michael Cullen
12 13 15

3
James Morley
41 56 76


Last edited on
Topic archived. No new replies allowed.