Problem reading file,searching, and displaying inf

2019-08-29 03:32发布

问题:

Sorry if this seems dumb, I am pretty new to C++ and I'm having trouble displaying information read into an array of structures from an input file.

I have 3 functions. One for reading my file into the array, one for prompting the user to search for a specific struct in the array, and the last one for displaying the contents of the array.

I don't know for certain if my readFile() or displayAllStudents() is broken. I am able to output the first line from the file, but the rest is 0. Next my selectStudent() is probably terrible; I'm having trouble finding a good solution for what I'm trying to accomplish.

I've tried many solutions posted here, but my issue seems unique so I'm hoping I can help pointed in the right direction.

Input file and desired output.


    ID   CLA   OLA   Quiz   Homework   Exam   Bonus   Total   FinalGrade   
c088801    10    15      4         15     56       5 
c088802     9    12      2         11     46       2 
c088803     8    10      3         12     50       1
c088804     5     5      3         10     53       3
c088805     3    11      1         10     45       0 
c088806     8    14      2         11     40      -1  
c088807     4    12      2         12     48      -2
c088808    10    10      3         11     36       0
c088809     8     8      3         11     39       0
c088810     6     9      4          9     47       3
c088811     8     7      3         13     41       3
c088812     4    11      3         11     37       1
c088813     9    15      2          8     50       2
c088814     8    12      2         10     48       4
c088815     6     8      1          7     45       1
c088816     7     7      2          6     51       2
c088817     8     9      2         12     38       2 
#include <iostream>
#include <fstream>
#include <string>

using namespace std;

struct Student {
    char ID[7];
    int CLA;
    int OLA;
    int Quiz;
    int Homework;
    int Exam;
    int Bonus;
    int Total;
    int FinalGrade;
};
const int SIZE = 20;

//Function prototypes
void readFile(Student[]);
int selectStudent(Student[]);
void displayAllStudents(Student[]);

int main() {
    Student Roster[SIZE] = {};                  //Initalizes array

    readFile(Roster);
    //selectStudent(Roster);
    displayAllStudents(Roster);

    system("pause");
    return 0;
}

//This function will read the text file into our array of structures.
void readFile(Student Roster[]) {
    ifstream inFile("point.dat");                   //Reads input file
    string line;

    getline(inFile, line);                          //Skips first line of file

    for (int i = 0; i < SIZE; i++) {                
            inFile >> Roster[i].ID >> Roster[i].CLA >> Roster[i].OLA >>
                Roster[i].Quiz >> Roster[i].Homework >> Roster[i].Exam >>
                Roster[i].Bonus >> Roster[i].Total >> Roster[i].FinalGrade;
    }

}

//This function will prompt user to select an individual student ID
//then display that student's information.
int selectStudent(Student Roster[]) {
    char* cNumber;

    cout << "Please enter the student's c-number:" << endl;
    cin >> cNumber;

    for(int i; i < SIZE; i++){
        if(strcmp(Roster[i].ID, cNumber)==0){
            return i;
        }
    }
    return -1;
}

//This function will display all our student information
void displayAllStudents(Student Roster[]) {
    cout << "     ID   CLA   OLA   Quiz   Homework   Exam   Bonus   Total   FinalGrade" << endl;

    for (int i = 0; i < SIZE; i++) {
        cout << Roster[i].ID[0] << Roster[i].ID[1] << Roster[i].ID[2] << Roster[i].ID[3] << Roster[i].ID[4] << Roster[i].ID[5] << Roster[i].ID[6]
            << " " << Roster[i].CLA << " " << Roster[i].OLA << " " << Roster[i].Quiz << " " <<
            Roster[i].Homework << " " << Roster[i].Exam << " " << Roster[i].Bonus << " " <<
            Roster[i].Total << " " << Roster[i].FinalGrade << endl;
    }
}


My output.

     ID   CLA   OLA   Quiz   Homework   Exam   Bonus   Total   FinalGrade
c088801 10 15 4 15 56 5 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
        0 0 0 0 0 0 0 0
Press any key to continue . . .

回答1:

In readFile, when you're reading the contents of your file here, you've assumed that all fields for every Student struct contain data.

        inFile >> Roster[i].ID >> Roster[i].CLA >> Roster[i].OLA >>
            Roster[i].Quiz >> Roster[i].Homework >> Roster[i].Exam >>
            Roster[i].Bonus >> Roster[i].Total >> Roster[i].FinalGrade;

Since your input doesn't have content in the Total and FinalGrade columns, when your program hits >> Roster[i].Total for the first time it's actually trying to read the second student's id, c088802, which isn't the integer value expected by Roster[i].Total.

If you know your input data will never have content in the Total and FinalGrade columns, you can remove >> Roster[i].Total >> Roster[i].FinalGrade from your file read loop.

If you know your input data might not be complete, but don't know how much of the row will be filled, something like this should work, though there's likely a better way.

for (int i = 0; i < SIZE; i++) {
    getline(infile, line);
    stringstream ss(line);

    ss >> Roster[i].ID;

    int value, count = 0;
    while(ss >> value){
        switch(count)
        {
           case 0: Roster[i].CLA = value;
           break;
           case 1: Roster[i].OLA = value;
           break;
           case 2: Roster[i].Quiz = value;
           break;
           ...
           case 7: Roster[i].FinalGrade = value;
           break;
        }
        ++count;
    }        
}


回答2:

IMHO, you should change your perspective and have the object read its members from the stream:

struct Student
{
    std::string ID;
    //...
    friend std::istream& operator>>(std::istream& input, Student& s);
};

std::istream& operator>>(std::istream& input, Student& s)
{
    input >> s.ID;
    input >> s.CLA;
    //...
    input >> s.FinalGrade;
    return input;
};

You can then read in the data with a simple loop:

std::vector<Student> database;
Student s;
while (infile >> s)
{
    database.push_back(s);
}

I've made two subtle changes here: 1) Use of std::string for the ID and 2) Using std::vector instead of an array.

The std::vector is an excellent data structure to use when reading from files, as it expands as necessary. With an array, you will have to keep checking for overflow and reallocating as necessary to accommodate new items.

I believe your issue is using operator>> with a character array. There is no method to limit the input to the size of your array. You may want to increase your array by 1 to allow for the terminating nul character (which may be appended by operator>>). The C-style strings (arrays of characters) are terminated by a nul character so you will always need that extra array slot.



回答3:

I fully agree with the statements from Thomas Matthews.

Additionally I would like to show a full example.

I packed all the student data and methods in one struct Student. All the integer data are stored in one std::vector and can be accessed via an index. This makes life easier.

Then we overload the extractor operator. With that, we can easily read the complete data for one student. For debug purposes, we overload also the inserter.

Please note: With that, reading of all student data into a vector is an one-liner, by simply using the vectors range constructor.

Also the output of all data is an ultra simple one-liner.

For the fun of it, I added also the printSelectedStudendData function.

Please see:

#include <iostream>
#include <vector>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <iomanip>

std::istringstream testDataFile(
R"#(c088801    10    15      4         15     56       5 
c088802     9    12      2         11     46       2 
c088803     8    10      3         12     50       1
c088804     5     5      3         10     53       3
c088805     3    11      1         10     45       0 
c088806     8    14      2         11     40      -1  
c088807     4    12      2         12     48      -2
c088808    10    10      3         11     36       0
c088809     8     8      3         11     39       0
c088810     6     9      4          9     47       3
c088811     8     7      3         13     41       3
c088812     4    11      3         11     37       1
c088813     9    15      2          8     50       2
c088814     8    12      2         10     48       4
c088815     6     8      1          7     45       1
c088816     7     7      2          6     51       2
c088817     8     9      2         12     38       2 
)#");

constexpr size_t NumberOfEntriesToRead = 6;

struct Student
{
    std::string ID;             // Student ID
    std::vector<int> data;      // Student related Data

    // Overload extractor operator >> to read all elements of a student
    friend std::istream& operator >> (std::istream& is, Student& s) {
        s.data.clear(); is >> s.ID; 
        std::copy_n(std::istream_iterator<int>(is), NumberOfEntriesToRead, std::back_inserter(s.data));
        return is;
    }

    // Overload inserter operator << to write all elements of a student to a stream
    friend std::ostream& operator << (std::ostream& os, const Student& s) {
        os << std::setw(10) << s.ID;    
        std::for_each(s.data.begin(), s.data.end(), [&os](int i) { os << std::setw(10) << i; });
        return os;
    }
};

void printSelectedStudendData(std::vector<Student>& vs)
{
    std::cout << "\nEnter the c-number of the student:\n\n";
    std::string cNumber{}; std::cin >> cNumber;
    std::vector<Student>::iterator found = std::find_if(vs.begin(), vs.end(), [&cNumber](const Student & s) {return s.ID == cNumber; });
    if (found != vs.end()) 
        std::cout << "\n\nData of student with c-Number '" << cNumber << "'  :\n\n" << *found << "\n\n\n";
    else 
        std::cout << "\n\nCould not find Student with c-Number '" << cNumber << "'\n\n\n";

}

int main()
{
    // Read all data in vector of students. Use vectors range constructor
    std::vector<Student> students{ std::istream_iterator<Student>(testDataFile), std::istream_iterator<Student>()};

    // Search for a student and show his data
    printSelectedStudendData(students);

    // Write all Data to console
    std::copy(students.begin(), students.end(), std::ostream_iterator<Student>(std::cout, "\n"));
    return 0;
}

I hope this helps . . .