0

for my csc 102 assignment, I need to create a class to hold a student's grades and name. Then output that information to a text file. The grades and name are both read from an input file. I got it to succesfully run one instance of student. However, I do not know how to make the next student object read from the next line. the input file is in this format:

Jonathan Blythe 87 76 79 88
Jessica Blake 87 79 58 86
Jonathan Lee 88 86 69 100
Joseph Blake 78 89 50 69

My first Student object Student a; reads the correct line. However, when I call the function again for another Student object Student b;, it still reads the first line, and overwrites the output file. I thought if I didn't close the file until the end of main that it may read correctly. I will show the class header file, and the implementation file for Student below.

#include "Student.h"

Student::Student() {
    cout << "Default Constructor" << endl;
}

void Student::getscores() {
    ifstream infile;
    infile.open("input.txt");

    infile >> firstName >> lastName;
    for (int i = 0; i <= 3; i++) {
        infile >> scores[i];
    }

    infile.close();
}

void Student::getaverage() {
    average = 0;
    for (int i = 0; i < 4; i++) {
        average = average + scores[i];
    }
    average = average / 4;
}

void Student::print()const {
    ofstream outfile;
    outfile.open("output.txt");
    outfile << firstName << " " << lastName << endl;
    cout << firstName << " " <<  lastName << endl;
    for (int i = 0; i <= 3; i++) {
        cout << scores[i] << " ";
        outfile << scores[i] << " ";
    }
    cout << endl;
    outfile << endl;
    cout << "Average Score: " << average << endl;
    outfile << "Average Score" << average << endl;
    cout << "Letter Grade: " << grade << endl;
    outfile << "Letter Grade: " << grade << endl;
    //outfile.close();
}

void Student::getletter() {
    if (average >= 90)
        grade = 'A';
    else if (average >= 80 && average < 90)
        grade = 'B';
    else if (average >= 70 && average < 80)
        grade = 'C';
    else if (average >= 60 && average < 70)
        grade = 'D';
    else if (average < 60)
        grade = 'F';
}

Student::~Student() {   
}

and

#pragma once
#include<iostream>
#include<string>
#include <fstream>
using namespace std;

class Student
{
    string lastName;
    string firstName;
    int scores[4] = { 0,0,0,0 };
    int average = 0;
    char grade = 'n';
public:
    Student();
    Student(string, string, int, int, int, int, char);
    ~Student();
    void getscores();
    void getaverage();
    void getletter();
    void print()const;
};
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • 3
    You forgot The Golden Rule Of Computer Programming: "your computer always does exactly what you tell it to do instead of what you want it to do". You told your computer that your `getScores()` should open the file, read what's in the first line of the file, and close the file. And, obviously, every time your `getScores()` gets called, that's exactly what happens: read the first line in the file. If you want your computer to do something different, in `getScores()`, you'll need to tell your computer exactly what `getScores()` should do. – Sam Varshavchik Mar 04 '20 at 04:06
  • I tried having it not close the file. This wouldn't be a problem for me if I wasn't required to set these students up as different objects of the class, which is the way I understand the professor wants it. – killerkody gaming Mar 04 '20 at 04:37
  • Note: how you read the name will fail when it encounters compound names like Victor von Doom, and if there's a name you do not want to get wrong, that the one. – user4581301 Mar 04 '20 at 04:38
  • I want to be able to call through a new Student object such as ```Student b;``` and call ```b.getScores``` without it resetting where it is reading from. I attempted to have ```infile``` as an object in main, but it will not allow me to pass infile as a parameter. – killerkody gaming Mar 04 '20 at 04:39
  • Suggestion: read all of the stuff you need from the file for one `Student` in `main` and then call the `Student(string, string, int, int, int, int, char)` constructor. Make your job easier. – user4581301 Mar 04 '20 at 04:39
  • Another good approach is to overload `operator>>`. Then you can `Student p; infile >> p;` Doesn't get much easier than that. Notes on how to overload the operator (and much more wisdom) to be found at [What are the basic rules and idioms for operator overloading?](https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading) – user4581301 Mar 04 '20 at 04:42
  • @user4581301 I don't know why I disregarded a parameter constructor. I initially had one, but for some reason I stuck to a default constructor. Thank you. This solved my current problem. – killerkody gaming Mar 04 '20 at 04:50
  • @user4581301 I would suggest you put in your answer as a submission as it is what solved my problem. I will mark it as correct answer. – killerkody gaming Mar 04 '20 at 04:59

2 Answers2

0

How do I read incrementally to the next line everytime a function is called?

One option is to pass the input stream as an argument to the function.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

You should read the input.txt line by line, for each line you need to parse to get the firstName, lastName, scores then use them to create a new object of Student class (you need some changes of Student class to create object from name, set scores, etc.)

I suggest the code skeleton is something like below:

char line[128] = {0,};
ifstream infile;

infile.open("input.txt");
if (!infile.is_open()) {
    return;
}            

while (infile.getline(line, sizeof(line) - 1)) { // read content of next line then store into line variable
    // parse content of line to get firstName, lastName, scores
    ...

    // create new object of Student class from firstName, lastName, scores you got
    ...

    // clear content in line
    memset(line, '\0', sizeof(line));
}
TuanPM
  • 685
  • 8
  • 29