0

I am attempting to extract data from a file and the files will contain data such as:

3

Jimmy Bob 40 60 70

Sarah Palin 70 80 30

Alex Trebek 90 100 90

Jimmy Turner 100 50 70

I am trying to grab the first and last name of each person within the file. Then calculate the averages of their grades. I am having a problem with separating the information to the respected data types (i.e, Put Jimmy in the firstname variable and Bob in the last name variable via an array). Lastly, I am trying to collect all the grades in a 2D array. How would I do this? I would imagine it is possible to read from a file and tell the program to skip spaces, but how?

// ###Function2### Extracts data from file and divides it to the appropriate data type.
void ReadFile(ifstream& InFile, int& line1, string firstname[][COL], string lastname[][COL], int grades[][COL]) {

    int row = 0, col;

    InFile >> line1;
    InFile.ignore(1000000, '\n');

    
    for (row = 0; row < line1; row++)
        for (col = 0; col < line1; col++) 
            getline(InFile, firstname[row][col], ' ');
            getline(InFile, lastname[row][col], ' ');
            InFile.ignore(1000000, '\n');


}

// ###Function3### Outputs the results from function2.
void PrintGrades(int& line1, string firstname[][COL], string lastname[][COL], int grades[][COL]) {

    int row = 0, col;

    for (row = 0; row < line1; row++)
        for (col = 0; col < line1; col++)
            cout << firstname[row][col] << " ";
            cout << lastname[row][col] << " ";
}

Could I possibly use:

void ReadFile(ifstream& InFile, int& line1, string firstname[COL], string lastname[COL], int grades[][COL]) {

    int row = 0, col = 0;

    InFile >> line1;
    InFile.ignore(1000000, '\n');

    
    for (row = 0; row < EOF; row++) 
        getline(InFile, firstname[row][col], ' ');
        getline(InFile, lastname[row][col], ' ');
        InFile.ignore(1000000, '\n');


}

to extract the file?

  • Does the following answer your question on removing spaces? https://stackoverflow.com/questions/216823/how-to-trim-an-stdstring – ChrisSc Nov 29 '22 at 00:08
  • Recommendation: Don't use a 2D array. Use a structure and an array of that structure. `struct person { string fname; string lname; int grades[3]; }; person people[MAX_PEOPLE];` – user4581301 Nov 29 '22 at 00:09
  • I've went through several forums for the past 2 hours and none of them have helped me. – LordOfChimichangas Nov 29 '22 at 00:11
  • And watch out for compound names like Victor Von Doom. That's one name you don't want to mess up. – user4581301 Nov 29 '22 at 00:11
  • It seems a bit naive to assume that everybody has two names. It would perhaps be better to keep reading strings and appending those to the person's _full_ name until you encounter one that's all digits. That will cover most scenarios, except maybe the offspring of dorks like Elon Musk. But honestly, who cares about them. – paddy Nov 29 '22 at 00:14
  • Use [option 2 of this linked answer](https://stackoverflow.com/a/7868998/4581301) as the basis for reading a person from the file. – user4581301 Nov 29 '22 at 00:14
  • @Paddy Elon, I hope. – user4581301 Nov 29 '22 at 00:15
  • If I use a 1D array for the grades, would I need to put the loop that reads the grades within a while-loop that stops at the end of the file? – LordOfChimichangas Nov 29 '22 at 00:15
  • Yup, but if you read line by line as suggested 3 comments up, the end of the file is the end of the line. You wrap that in a while loop grabbing lines and yousa done! – user4581301 Nov 29 '22 at 00:16
  • Wow, thank you so much! That actually makes a lot more sense. Also, I appreciate the funny comments. – LordOfChimichangas Nov 29 '22 at 00:20
  • If you will always have only a `first` and `last` name, then you are fine, but names can have middle names and last names can have two parts. One way to generically handle all is to `.find_first_of()` using `"0123456789"` as the constant char type to locate the first digit and then backup to the last character. – David C. Rankin Nov 29 '22 at 01:18

1 Answers1

2

A lot of good comments here on how to make this more general, but I'm gonna recommend a simple struct with an overloaded operator>> and assume you really only need to handle two names and three grades per line.

struct Record {
    std::string first_name;
    std::string last_name;
    int grade1;
    int grade2;
    int grade3;
};

std::istream& operator>>(std::istream& in, Record& record) {
    return in >> record.first_name >> record.last_name
        >> record.grade1 >> record.grade2 >> record.grade3;
}

This lets us write code like

Record rec;
input >> rec;

And if you want to do something fancier, you can use an istream_iterator to read the whole vector with a one-liner

 std::vector<Record> records{
   std::istream_iterator<Record>{input},
   std::istream_iterator<Record>{}};

Here's a live example with std::cin, but drop in any other istream and it'll work the same


For a version with an array instead of three grade variables

struct Record {
    std::string first_name;
    std::string last_name;
    std::array<int, 3> grades;
};

std::istream& operator>>(std::istream& in, Record& record) {
    return in >> record.first_name >> record.last_name
        >> record.grades[0] >> record.grades[1] >> record.grades[2];
}

live link

Ryan Haining
  • 35,360
  • 15
  • 114
  • 174
  • I'd turn the `gradeN` variables into an array (`vector` if you got'em and don't know how many grades are coming your way) because then you can easily `int& operator[](size_t index) { return grades[index]; }` and use your container of `Record`s like a 2D array (`recs[1][2]` gets the third grade from the second record) to keep a 2D-array hungry instructor at least sort of happy. – user4581301 Nov 29 '22 at 00:52
  • @user4581301 I added one with `std::array`. `operator[]` doesn't belong here but you're right that it might work to satisfy a prof – Ryan Haining Nov 29 '22 at 00:57