Reading a file with mixed data types on each row

[p] So I've been working on some code that will read the data off a file, which looks like:


2 apple
1 house 0 3 0
1 shed 0 4 0
3 car
1 city 4 5 1
1 town 3 4 1
4 t j johnson

Now the file is bigger then this so will put the rest in if needed. but it goes in this order of sequence.

I have code that can take the file out as a string but that means i cant touch the ints in there. so i have been working on using a vector to take each item out and store it through a class.


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
  class info{
public:
	info(int t, string p, int pr, int r, int g);//overload
	int gettype();
	string getName();
	int getnum1();
	int getnum2();
	int getnum3();
	void setType(int t);
	void setname(string p);
	void setnum1(int r);
	void setnum2(int pr);
	void setnum3(int g);

private:
	int type;
	string name;

	int num1;
	int num2;
	int num3;
};
bool getfilecontent(std::string Text, vector<info>& newinfo)
{
	std::ifstream in(Text.c_str());//read in file and fill vector
	if (!in) 
	{
		cerr << "cannot open file" << endl;
		return false;
	}
	int ntype = 0;
	std::string str;
	int pri = 0;
	int nr = 0;
	int ng = 0;
	string a;
	while (in >> ntype>>a>>	str>> pri>> nr>> ng)
	{
		for (int x = 0; x < 26; x++)
		{
			info Newinfom(
				ntype, str, pri, nr, ng);

			newinfo.push_back(Newinfom);
			int i = 0;
			cout << Newinfo[i].getname() << endl;
			i++;

		}
	}
	in.close();
	return true;
void printv(vector<info>&Newinfo);
int main() {
	vector<info> myinfo;

	bool result = getfilecontent("Text.txt", myinfo);
	if (result)
	{
		std::cout << "The board has been loaded successfully - you are now able to play:" << std::endl;

	}
	printv(myinfo);

	system("pause");
}

void printv(vector<info>&newinfo) {
	int size = 26;
	for (int i = 0; i < size; i++) {
		cout << newinfo.at(i).getname(); 

//Unhandled exception at 0x743418A2 in Project9.exe: Microsoft C++ exception: //std::out_of_range at memory location 0x007BF658. happens here

	}

}

Correct me if im wrong but is this error because the first line only has an int and a string ?
My question is, is it possible to read the first int and decide whats on that line and how to read it.
i.e.
sees that first int is 2 and knows there is only an int and a string and reads them. then sees the first int second row is 1 and then knows that there is another 3 ints to read. and then the last one with 3 strings.
i dont want to be writing code for each line just what to do if it sees certain ints at the start.

if you need anything else just tell me and ill get back.
also haven't but the class functions in as i believe that i have got them correct.
thank you![p]
Since your lines seem to have different amounts of data I would recommend using a stringstream to parse each line, this will help keeping the stream in an error free condition. Read a full line using getline, then process that line with the stringstream.

My question is, is it possible to read the first int and decide whats on that line and how to read it.

Yes. If working from a stringstream just read the first number: stream >> number;. Then test that number and do the proper extraction of the rest of the entries as required.

//Unhandled exception at 0x743418A2 in Project9.exe: Microsoft C++ exception: //std::out_of_range at memory location 0x007BF658. happens here


If your vector doesn't have 26 elements then you will try to access the vector out of range. You would be better off using the size of the vector for your loop instead of some "magic number" like 26.

1
2
3
4
5
6
7
8
9
void printv(const vector<info>&newinfo) {  // Notice the const, a print function should not modify this vector.
	//int size = 26; // Not needed, a std::vector knows it's size.
	for (size_t i = 0; i < newinfo.size(); i++) {  // Notice the size_t, this is what std::vector.size() returns.
		cout << newinfo[i].getname();  // Note just using "array notation" because the "size" is already being checked.

// Or if using modern C++:
        for(auto& itr : newinfo
                cout << itr.getname();


It might be more help to understand what this file represents.

If each record has a specific format, and only needs to be recognized, then that suggests a more convenient solution in terms of polymorphic (or discriminated) behavior.

In other words, will a “house” record always be of the form:

    number house number number number

?
Thank you! just making sure i have the right idea.

so first i need to get the file as an entire string storing it in a vector, then parse through the vector checking the first number , then use if statements to go through the rest of the stream? outputting the data into the class vector as above



My question is, is it possible to read the first int and decide whats on that line and how to read it.

Yes, if the first number determines the format of the line.

Use a stringstream as jlb suggested and a switch statement.

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
bool getfilecontent(std::string Text, vector<info>& newinfo)
{
    std::ifstream in(Text.c_str());//read in file and fill vector
    if (!in)
    {
        cerr << "cannot open file" << endl;
        return false;
    }
    int ntype = 0;
    std::string str;
    int pri = 0;
    int nr = 0;
    int ng = 0;
    string a;
    string line;
    while (in >> line)    // Read the entire line
    {   stringstream ss(line);    // Convert to a stringstream
        ss >> ntype;    //   Read the first number
        switch (ntype)
        {
        case 1:
            ss >> str >> pri >> nr >> ng;
            break;
        case 2:
            ss >> str;
            break;
        case 3:
            ss >> str;
            break;
        case 4:
            //  read 3 strings from ss
            break;
        default:
            // handle if not 1-4
        }
        //  You don't want a loop here
        info Newinfom(ntype, str, pri, nr, ng);
        //  If you don't read pri, nr, ng, you're going to be pushing obsolete values
            newinfo.push_back(Newinfom);
    }
    //  Now you can loop and print the vector
    in.close();
    return true;
}

Last edited on
No one has suggested that.
so first i need to get the file as an entire string storing it in a vector,

Possibly, but not necessarily. There are multiple ways of processing the file it really depends on what that data represents.


@Dunthomhas
Yes, but the ones with that format are all called something different. the first number denotes what the format of that line is

1 house 0 3 0
1 means its number house number number number

2 and 3 mean number name

and 4 is number name name name

however 4 is the only one of its kind in the file

thank you guys. you the real mvp
while (in >> line) // Read the entire line

That will not read an entire line if there are any embedded white space characters. Using getline() would probably be better.

I would also recommend considering placing the data variable definitions inside the while loop so that they are cleared each time through the loop.




so i added 2 extra strings a and b for case 4

so the obj to be added to the vector is now

info newinfom (ntype , str ,a,b,pri,nr,ng)

now how do i add them to the vector as at the moment its not, now should add default values to the variable not being used ie 0 for all ints after the name or is there a better way of doing this ?
It would still be nice to know what this is for.

The current effort is over a flawed design pattern. Perhaps we can suggest something that will better represent whatever it is you are trying to do.
yeah i probably should have included that.
the vector is a board and the strings are the places, and the ints are the points you receive based on the number of goes you have had.
i have a random num gen in a different file to use to get round the board
Ok, that makes life a lot simpler. Why is the "t j johnson" piece different?
you get a random amount of points if you land on it, theoretically from him
That is a simple specialization then.

I would store the pieces as a structure that looks something like:

1
2
3
4
5
struct Piece
{
  std::string name;
  std::vector <int> points;
};

And a constant value for the TJ Johnson piece:
 
Piece TJJohnson { "t j johnson" };

You can compare pieces:

1
2
  if (my_piece == TJJohnson) ...
  else ...


Everything else is easily loaded from stringstream with the same code. Again you’ll need to specialize on the "t j johnson" piece, but that isn’t hard when you are dealing with a string.

Hope this helps.
Topic archived. No new replies allowed.