Debug error: out_of_range at memory location

Hello, folks. I am getting a debug error in a program for parsing entries in a text file. This is an assignment for a class and I have no experience using a debugger. My program successfully parses and outputs what I want but I am struggling with understanding how to fix this debug error.

This is the line that the debugger breaks at with an error:
 
line = line.substr(line.find(str1) + 2);


If I replace with "line = line.substr(line.find(str1) + 1);" no error occurs but I am left with spaces in beginning my strings after the first.

The debug error:
 
First-chance exception at 0x7644C42D in ConsoleApplication3.exe: Microsoft C++ exception: std::out_of_range at memory location 0x003DEE64.


Source:
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
//Sources: none
//Sean McKee
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
struct studentType {
	string studentFName;
	string studentLName;
	int testScore;
	char grade;
};
void parseData(ifstream& infile, studentType students[], int number_of_students);

int main() {
	const int number_of_students = 20;
	studentType students[number_of_students];

	ifstream infile;
	ofstream outfile;
	infile.open("input.txt");
	outfile.open("output.txt");

	parseData(infile, students, number_of_students);

	infile.close();
	outfile.close();

	return 0;
}

void parseData(ifstream& infile, studentType students[], int number_of_students) {
	string line, fname, lname, junk1, junk2, score, grade;
	string str1(", ");
	int result;
	char mark;

	while (infile.peek() == '#')
		infile.ignore(256, '\n');

	for (size_t i = 0; i < number_of_students; i++) {
		getline(infile, line);

		fname = line.substr(0, line.find(str1));
		students[i].studentFName = fname;
		cout << students[i].studentFName << endl;
		line = line.substr(line.find(str1) + 2);

		lname = line.substr(0, line.find(str1));
		students[i].studentLName = lname;
		cout << students[i].studentLName << endl;
		line = line.substr(line.find(str1) + 2);

		junk1 = line.substr(0, line.find(str1));
		cout << junk1 << endl;
		line = line.substr(line.find(str1) + 2);

		junk2 = line.substr(0, line.find(str1));
		cout << junk2 << endl;
		line = line.substr(line.find(str1) + 2);

		score = line.substr(0, line.find(str1));
		cout << score << endl;
		line = line.substr(line.find(str1) + 2);
		
		grade = line.substr(0, line.find(str1));
		cout << grade << endl;
	}
}


input.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#First Name, Last Name, Class, Instructor, Test Score, Grade
#Lines that begin with a # are comments, not data, and should be ignored
Jonathan A., Smith, CS 161, Jones, 90, A
Beatrice G., Smythe-Wallace, CS 161, Iyer, 100, A
Sara, Michaels Dunthorpe, CS 161, Trigoboff, 85, B
Sarah, Dunthorpe Michaels, CS 161, Jones, 90, A
Stephen, Wolfram, CS 161, Insalata, 100, A
Michael, Dunce Mann, CS 161, Jones, 50, F
Julie Lynn, Rojas, CS 161, Liang, 75, C
Rosita, Riveter, CS 161, Jones, 80, B
Manfried, Von Hungerstein, CS 161, Insalata, 85, B
Li Yun, Kwan, CS 161, Liang, 86, B
Abraham, Lincoln, CS 161, Iyer, 90, A
Patrick P., Kennedy, CS 161, Trigoboff, 80, B
Stephen, Alpha, CS 161, Insalata, 100, A
Dunthorpe, Mann, CS 161, Jones, 50, F
Miguel Rojas, Blanco, CS 161, Liang, 75, C
Jaime, Riveter, CS 161, Jones, 80, B
Sigfried, Von Hungerstein, CS 161, Jones, 76, B
Tippoli, Inyala, CS 161, Liang, 90, A
Last edited on
And what is the value of line at the time of the crash?

Instead of using the substr() function have you thought of using a stringstream and getline() with the optional third parameter, the delimiter?

Be careful of using the return value of find() in the first argument in your substr() call, remember if the find fails the value returned is std::string::npos, which will be out of the range of your string.

The debugger breaks at line 49.

I am using substr() because it is what the instructor has asked for in the previous assignment. This is my first term studying C++ so my knowledge and expected resources are basic.

Okay. I think I understand your warning. However, I am uncertain of how to parse the string in another way. I will give it some thought.
The debugger breaks at line 49.

And what is the value of line right before line 49. What student record is being parsed (value of i?)?

By the way since your sample file doesn't contain 20 students what happens when the getline() fails?

You really should probably be using a while() loop with the file reading getline() controlling the loop.

I also recommend that you create a std::string::size_type to hold the return value of your find() calls and use that variable, after insuring it is within the range of your loop, in your substr() calls.

1
2
3
4
5
6
7
8
9
10
11
12
13
...
    std::string::size_type pos;

    for (size_t i = 0; i < number_of_students; i++) { 
        getline(infile, line);

        pos = line.find(str);
        if(pos > line.length())
            // Error do something about the problem.
        
        fname = line.substr(0, pos);
        students[i].studentFName = fname;
...
When getline() failed it was returning an empty string/"" and causing the error.

I restructured my loop and used a while() loop like you suggested. I also used a variable to store the position of my find() calls. I understand how the if() statement checks if pos is within the length of the string, but I am unsure what to do if it returns an error.

Following your advice I was able to fix the debug error and am on to the next hurdle. I successfully output the desired data within the while() loop, but attempting to output outside the while() loop outputs the last student on the list followed by strange characters/numbers. I feel like I am so close to getting this right but something obvious is eluding me.

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
...
for (size_t i = 0; i < number_of_students; i++) {
	while (getline(infile, line).good()) {
		pos = line.find(str1);
		students[i].studentFName = line.substr(0, pos); //first name
		line = line.substr(pos + 2);
		pos = line.find(str1);
		students[i].studentLName = line.substr(0, pos); //last name
		line = line.substr(pos + 2);
		pos = line.find(str1);
		line = line.substr(pos + 2);
		pos = line.find(str1);
		line = line.substr(pos + 2); //skip class and instructor
		pos = line.find(str1);
		students[i].testScore = stoi(line.substr(0, pos)); //test score
		line = line.substr(pos + 2);
		pos = line.find(str1);
		students[i].grade = line.substr(0, pos)[0]; //grade

		cout << students[i].studentFName << ", "
			 << students[i].studentLName << ", "
			 << students[i].testScore << ", "
			 << students[i].grade << endl; //successfully outputs the parsed data
	}
}

for (size_t i = 0; i < number_of_students; i++) {
	cout << students[i].studentFName << ", "
		<< students[i].studentLName << ", "
		<< students[i].testScore << ", "
		<< students[i].grade << endl; //outputs the last student then a bunch of garbage
}
...


output.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Tippoli, Inyala, Z, A
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
You don't need the for loop however you will then need to declare i and increment it in the while loop.

Something more like:
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
...
size_t actual_students = 0;
std::string::size_type pos;

while (getline(infile, line) && actual_students < number_of_students)
{
   pos = line.find(str1);
   if(pos > line.length())
      break;
   students[actual_students].studentFName = line.substr(0, pos); //first name
   line = line.substr(pos + 2);
   pos = line.find(str1);
   students[actual_students].studentLName = line.substr(0, pos); //last name
   line = line.substr(pos + 2);
   pos = line.find(str1);
   line = line.substr(pos + 2);
   pos = line.find(str1);
   line = line.substr(pos + 2); //skip class and instructor
   pos = line.find(str1);
   students[actual_students].testScore = stoi(line.substr(0, pos)); //test score
   line = line.substr(pos + 2);
   pos = line.find(str1);
   students[actual_students].grade = line.substr(0, pos)[0]; //grade

   cout << students[actual_students].studentFName << ", "
        << students[actual_students].studentLName << ", "
        << students[actual_students].testScore << ", "
        << students[actual_students].grade << endl; //successfully outputs the parsed data
   ++actual_students;
}

for (size_t i = 0; i < actual_students; i++)
{
   cout << students[i].studentFName << ", "
        << students[i].studentLName << ", "
        << students[i].testScore << ", "
        << students[i].grade << endl; //outputs the last student then a bunch of garbage
}


You should also be returning the actual_students from the function to be used in the rest of your loops. And I only added one check for the return from the find() function, you need to make sure you check before you use the value in your substr() calls.

I successfully output the desired data within the while() loop, but attempting to output outside the while() loop outputs the last student on the list followed by strange characters/numbers. I feel like I am so close to getting this right but something obvious is eluding me.

Where are you writing your output file?

Topic archived. No new replies allowed.