ifstream

I need some help modifying this program so that it can read a file with invalid data. The invalid data is in a file that looks like this:

Robert Smith S45 78Y W91
Susan Watson HH95DF E92DD P88D
Steve Jones
Jimmy Anderson SSF54S W89W U66T DD44S DD21S
Matt Duncan WQ72Q A81B EE89W

The program is supposed generate a message printing the name of the students and the fact that there are no scores for that student. Also, for some reason I can't generate an output file with the greeting, students' name, their overall average and grade, and the farewell message. The output file is just empty. Thank you for taking the time to look into my program. Any help will be greatly 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

#include <iostream>
#include <string>
#include <fstream>
#include <iomanip>
using namespace std;
void greeting();
void getName(ifstream& in, string& fname, string& lname);
void readScore(ifstream& in, double& sum, int& counter);
double calculateAverage(double sum, int counter);
char assginGrade(double avg);
void printData(string fname, string lname, double sum, int counter, double avg, char grade);
void farewell();
int main()
{
 string fname = string();
 string lname = string();
 double sum = double();
 int counter = int();
 double avg = double();
 char grade = char();
 string filename = string();
 string outfile = string();
 
 greeting();
//Input file
 cout << "Please enter the name of the file: ";
 cin >> filename;
 ifstream in;
 in.open(filename + ".txt");
  if (!in)
  {
   cout << "File does not exist!" << endl;
  }
  else
  {
  in.peek();
  if (in.eof())
  {
   cout << "File is Empty." << endl;
  }
  else
  {
//Output file
 cout << "Please enter the name of the print file: ";
 cin >> outfile;
 ofstream fout;
 fout.open(outfile + ".txt");
  if (!fout)
  {
   cout << "File does not exist!" << endl;
  }
  else
  
   //Functions
   while (!in.eof())
   {
    sum = 0.0;
    counter = 0;
    getName(in, fname, lname);
    readScore(in, sum, counter);
    avg = calculateAverage(sum, counter);//214//3
    grade = assginGrade(avg);
    printData(fname, lname, sum, counter, avg, grade);
   }
   farewell();
  }
 }
 
}
void greeting()
{
 
 cout << "\t\t\t\t********************************************" << endl;
 cout << "\t\t\t\tWelcome to my Grade Calculation System (GCS)" << endl;
 cout << "\t\t\t\t********************************************" << endl;
 }

void getName(ifstream& in, string& fname, string& lname)
{
 in >> fname >> lname;
}
void readScore(ifstream& in, double& sum, int& counter)
{
 double score = double();
 for (int i = 0; i < 3; i++)
 {
  in >> score;
  while (in.fail())
  {
   //cout << "bad score" << endl;
   in.clear();
   in.ignore(1, '\n');
   in >> score;
  }
  sum = sum + score;
  ++counter;
 }
}
double calculateAverage(double sum, int counter)
{
 
 double avg = double();
 avg = sum / counter;
 return avg;
}
char assginGrade(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 farewell()
{
 cout << "bye!" << endl;
}
void printData(string fname, string lname, double sum, int counter, double avg, char grade)
{
 cout << fname << " " << lname
  << " " << sum << setprecision(4) << " " << counter
  << " " << avg << setprecision (4) << " " << grade << endl;
 
}
What makes that data invalid? Do you have an example of valid data? vs invalid? and can you explain how the scoring works?
Last edited on
It looks as though the file contains all the data for a particular student on a single line?

If that is the case then read the file a whole line at a time.
1
2
3
4
5
6
7
8
9
    string line;
    while (getline(in, line)
    {
        // parse the line using stringstream
        istringstream ss(line);
        getName(ss, fname, lname);
        // etc.

    }

(header <sstream> neds to be #included).

Note that functions getName() and readScore() would need a very minor change to the parameter types.
Instead of ifstream& in put just istream& in (without the 'f' so it will work with any input stream, whether a file or a stringstream etc.


Bdanielz wrote:
Do you have an example of valid data?

Important question. That's something I'd like to know too.
Last edited on
First of all, thank you, Bdanielz and Chervil, for the help. By invalid data, I meant that Steve Jones doesn't have his scores display. For example, valid data would look like this:

Robert Smith 45 78 91
Susan Watson 95 92 88
Steve Jones 68 74 54
Jimmy Anderson 54 89 66 44 21
Matt Duncan 72 81 89

And, this is the invalid data that I have to work with:

Robert Smith S45 78Y W91
Susan Watson HH95DF E92DD P88D
Steve Jones
Jimmy Anderson SSF54S W89W U66T DD44S DD21S
Matt Duncan WQ72Q A81B EE89W

The way that this program should work is that it takes the name of students, their score, gets the average of the score, counts the number of test scores, and it gives it a grade. All data is on a .txt file. But, by the time that it reads Steve Jones' scores, since he does not have any scores, the program displays some funky names and scores after the scores of Susan Watson are display.

So, I need to make changes to my program so that it can read and display the name of Steve Jones, and the fact that he doesn't have any scores. And, then all that information has to be display on a output file.
Assuming the data follows this pattern.

{{first name}} {{space}} {{last name}} {{space}} {{Scores}}

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

using namespace std;

int main() {


    std::ifstream input("dataFile");

    // For each line in the file
    for (std::string line; getline(input, line);) {

        // int to count spaces in each line
        int spaceCount = 0;

        // For each char in in each line
        for (const char c: line) {

            // if true; increase spaceCount(0) by 1
            if (c == ' ')
                spaceCount++;
        }

        // if more than one space, we have valid data. 
        if (spaceCount > 1) {
            cout << "Valid data: " << line << endl;
        }


    }


    return 0;
}
Valid data: Robert Smith S45 78Y W91
Valid data: Susan Watson HH95DF E92DD P88D
Valid data: Jimmy Anderson SSF54S W89W U66T DD44S DD21S
Valid data: Matt Duncan WQ72Q A81B EE89W
Last edited on
Sometimes when dealing with invalid data, one has to consider very many different things which could go wrong. However in this example it seems there are just two scenarios. The Firstname and Lastname will always be present. After that, we have either a set of up to five valid scores, or no valid scores at all. (I don't see any examples where just some of the scores are valid?). If this is so, then the count of number of scores can be used as an error indicator - zero scores == error.

Following up on a previous suggestion, You could replace your main loop:
1
2
3
4
5
6
7
8
9
10
   while (!in.eof())
   {
    sum = 0.0;
    counter = 0;
    getName(in, fname, lname);
    readScore(in, sum, counter);
    avg = calculateAverage(sum, counter);//214//3
    grade = assginGrade(avg);
    printData(fname, lname, sum, counter, avg, grade);
   }


with the following
1
2
3
4
5
6
7
8
9
10
11
12
13
        string line;
        while (getline(in, line))
        {
            istringstream ss(line);
            getName(ss, fname, lname);
            readScore(ss, sum, counter);
            avg = calculateAverage(sum, counter);  
            grade = assginGrade(avg);
            if (counter)
                printData(cout, fname, lname, sum, counter, avg, grade );
            else
                printError(cout, fname, lname);
        }


This requires the header #include <sstream> for the stringstream.


Function prototypes:
1
2
3
void getName(istream& in, string& fname, string& lname);
void readScore(istream& in, double& sum, int& counter);
void printData(ostream& out, string fname, string lname, double sum, int counter, void printError(ostream& out, string fname, string lname);


Function definitions:
1
2
3
4
5
6
7
8
9
10
11
12
void readScore(istream& in, double& sum, int& counter)
{
    double score = 0;
    sum     = 0;
    counter = 0;
    
    while (in >> score)
    {
        sum += score;    
        counter++;
    }    
}


Note, I moved the setting of sum and counter to zero inside the function. Previously that was done in main() instead, but that means the function was not self-contained, it depended for successful operation on the user to carry out those operations, which would be a poor design.

1
2
3
4
5
void printError(ostream& out, string fname, string lname)
{
    out << left << setw(25) << (fname + ' ' + lname) << right
        << "Error, no scores found" << endl; 
}

Above, (and in function printData()) I have added the parameter ostream& out. That adds flexibility, if you want the output displayed on the console, pass cout as the argument. To send the output to a file, pass the name of the ofstream fout.


After a bit of reconsideration (and also looking at Bdanielz ideas), it would probably be better if the function printError displayed the whole of the line, rather than only the firstname/lastname. That's simple enough since the variable string line contains the entire line.
Last edited on
Topic archived. No new replies allowed.