Debug error: out_of_range at memory location

Jun 6, 2015 at 11:07pm
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 Jun 6, 2015 at 11:08pm
Jun 6, 2015 at 11:42pm
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.

Jun 6, 2015 at 11:49pm
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.
Jun 7, 2015 at 12:55am
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;
...
Jun 7, 2015 at 9:37am
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
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
, , Ì, Ì
Jun 7, 2015 at 12:23pm
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.