output stuck after first cycle

Hello, i need help with a class assignment:

I need to read a data file and use the data from it while separating it in to functions. It needs to be able to ignore bad data if they are present.

The data i am trying to output is first name, last name, average, and letter grade.
gooddata.txt

Mickey Mouse 45 78 91
Minnie Mouse 95 92 88
Donald Duck 72 81 89


baddata.txt

Mickey Mouse S45 78Y W91
Minnie Mouse HH95DF E92DD P88D
Jack Robinson
Jimmy Johnson SSF54S W89W U66T DD44S DD21S
Donald Duck WQ72Q A81B EE89W

The program runs fine with the gooddata.txt, but when i run the baddata.txt throught it it get caught up after the first run through the loop. i don't understand why this is. Can someone help me solve the problem and explain what i am doing wrong? Any help i much appreciated.


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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <vector>
using namespace std;

//********************PROTO****************************8
void greeting(ofstream& out);
void getName(ifstream& in, string& firstName, string& lastName);
void readData(ifstream& in, double& sum, int& count);
void reportNoScore(ifstream& in, string& firstName, string& lastName);
double calculateAverage(double& sum, int& count);
void printData(string&, string&, double&, char);
char assignGrade(double);



//******************** MAIN FUNCTION///////////////////////////
int main()
{
	string inputFile = "";
	string outputFile = "";
	string line;
	string fName = "";
	string lName = "";
	double sum = double();
	int count = int();
	char grade = ' ';
	double avg = 0.00;


	cout << "What is the input file name? " << endl;
	cin >> inputFile;
	inputFile += ".txt";
	
	ifstream in;
	in.open(inputFile.c_str());

	if (!in) //if in file does not exist do this
	{
		cout << "The input file does not exist, this program wil end.";
	}

	else
	{
		cout << "What will output file be name? ";
		cin >> outputFile;
		outputFile += ".txt";
		ofstream out;
		out.open(outputFile.c_str());
		if (!out)
		{
			cout << "The output file does not exist, this program wil end.";
		}
		else
		{
			greeting(out);
			while (!in.eof())
			{

				getName(in, fName, lName);
				readData(in, sum, count);

				if (sum == 0)
				{
					reportNoScore(in, fName, lName);
				}
				
				avg = calculateAverage(sum, count);
				grade = assignGrade(avg);
				
				printData(fName, lName, avg, grade);
				
									
				
			}


		}
	}
	system("PAUSE");
}

//*****************************MODULES********************************************************
void greeting(ofstream& out)
{
	cout << "Hello, this program will calculate the average and grade a student from file." << endl;
	out << "Hello, this program will calculate the average and grade a student from file." << endl;

}


void getName(ifstream& in , string& firstName, string& lastName)
{
	in >> firstName >> lastName;	
	while (in.fail())
	{
		in.clear();
		in.ignore(1, '\n');
		in >> firstName >> lastName;
	}
}

void readData(ifstream& in, double& sum, int& count)
{
	double tempSum = double();	
	sum = 0.0;
	count = 0;

	while ((in.peek() != '\n') && (!in.eof())) //.peek() looks at next character without using it, so this checks for end of line
	{
		in >> tempSum;
		while (in.fail())
		{
			in.clear();
			in.ignore(1, '\n');
			in >> tempSum;
		}
		sum = sum + tempSum;
		++count;
	}
				
}

void reportNoScore(ifstream& in, string& firstName, string& lastName)
{
	cout << "No score found for " << firstName << " " << lastName << endl;
}

double calculateAverage(double& sum, int& count)
{

	return sum / static_cast<int>(count);
}
char assignGrade(double avg)
{
	if (avg >= 90)
		return 'A';
	else if (avg >= 80)
		return 'B';
	else if (avg >= 70)
		return 'C';
	else if (avg >= 60)
		return 'D';
	else if (avg < 60)
		return 'F';
}

void printData(string& firstName, string& lastName, double& Avg, char grade)
{

	
	cout << firstName << " " << lastName << " " << Avg << " " << grade << endl;
}


Last edited on
I've been messing with it for many hours, across 3 days now. I've no idea whats wrong.
I've tried adding:
1
2
3
4
5
6
while (in.fail())
			{
				in.clear();
				in.ignore(1, '\n');
				in >> tempSum;
			}

to various places thinking maybe some of the bad data was catching the loop.
I also have messed with the loops in the main function aswell as in the other functions.

Any help or ideas are very welcome.
You use:
getName to input first and last names, then
readData to input some numbers (you hope).

If getName fails it goes to the next line and tries again. This is fine.
If readData fails ... it also goes to the next line ... but you won't find numbers there, you find strings.

If I were you I would have a single input routine trying to read firstName lastName and the three scores. If any of those fail then go to next line.
Last edited on
ok so i changed the readData() and getName() functions so i would be able to tell what data is gets stuck on.

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
void getName(ifstream& in, string& firstName, string& lastName)
{
	char a;
	in >> firstName >> lastName;
	cout << "sucess" << firstName << lastName << endl;
		
}

void readData(ifstream& in, double& sum, int& count)
{
	double tempSum = double();
	sum = 0.0;
	count = 0;

	while ((in.peek() != '\n') && (!in.eof())) //.peek() looks at next character without using it, so this checks for end of line
	{
		in >> tempSum;
		while (in.fail())
		{
			in.clear();
			in.ignore(2, '\n');
			cout << "fail!" << endl;
			in >> tempSum;
			system("PAUSE");
		}
		sum = sum + tempSum;
		++count;
	}

}


This code confirms that it failed in the second cycle when it trys to read the scores. How do i change the code to ignore the the first character in the data and try to read the next. For example in HH95DF, i want it to read and then run in.fail() till it gets to 95 and then take the value for 95 and use it.



while (in.fail())
{
in.clear();
in.ignore(1, '\n');
cout << "fail!" << endl;
}

I thought this would ignore bad data 1 character till it got good data or till it reached \n.
Last edited on
What I recommend doing is to check if the input is valid via a single routine like @lastchance said. I have it fixed if you'd like to see it otherwise I can give you a couple of hints. If you want hints here you go, using the stringstream we can parse data and check for valid input. You can do this by storing whole lines into the stringstream and then extracting the output. If you need more info on stringstream here, http://www.cplusplus.com/reference/sstream/stringstream/?kw=stringstream or you can ask me for a bit more help.

Last edited on
May be simpler to read each field as a string and then try to extract the integers from the strings.


Something along these lines, 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
#include <iostream>
#include <string>
#include <cctype>
#include <sstream>

// try to extract the number embedded in a string; 
// return true if the number in str was extracted and stored in n
bool get_number( const std::string& str, int& n )
{
    // get to the position of the first decimal digit
    std::size_t pos_first_digit = 0 ;
    for( char c : str ) if( !std::isdigit(c) ) ++pos_first_digit ; else break ;

    // try to convert the digits from thereon to an int.
    // may throw if there are no digits or
    // if there are too many digits (number exceeds the range of int)
    try { n = std::stoi( str.substr(pos_first_digit) ) ; return true ; }

    catch( const std::exception& ) {} // zero or too many digits, return false
    return false ;
}

constexpr std::size_t NUM_SCORES = 3 ;

bool get_scores( const std::string (&fld)[NUM_SCORES], int (&score)[NUM_SCORES] )
{
    for( std::size_t i = 0 ; i < NUM_SCORES ; ++i )
        if( !get_number( fld[i], score[i] ) ) return false ; // badly formed fld, return false

    return true ; // numbers were extracted from all flds
}

int main()
{
    // use a string stream instead of a file for testing the code
    std::istringstream file( "Mickey Mouse S45 78Y W91\n"
                             "Minnie Mouse HH95DF E92DD P88D\n"
                             "Jack Robinson\n"
                             "Jimmy Johnson SSF54S W89W U66T DD44S DD21S\n"
                             "Donald Duck WQ72Q A81B EE89W\n" ) ;

    std::string line ;
    while( std::getline( file, line ) ) // for each line in the file
    {
        std::istringstream stm(line) ; // create a stream to read from the line
        std::string fname, lname, flds[NUM_SCORES], dummy ; // dummy to test for spurious extra flds

        // if exactly five flds were read and there is no sixth fld
        if( stm >> fname >> lname >> flds[0] >> flds[1] >> flds[2] && !( stm >> dummy ) )
        {
            int scores[NUM_SCORES] ;
            if( get_scores( flds, scores ) ) // if scores were extracted from the flds
            {
                // well-formed line: print the data
                std::cout << fname << ' ' << lname << ' ' ;
                for( int s : scores ) std::cout << s << ' ' ;
                std::cout << '\n' ;
            }
        }
    }
}

http://coliru.stacked-crooked.com/a/b86ea5b2642f94f3
Topic archived. No new replies allowed.