简体   繁体   English

在C ++中读取文件,搜索并将信息显示到结构数组时出现问题

[英]Problem reading file,searching, and displaying information to an array of structures in C++

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. 抱歉,如果这看起来很愚蠢,我是C ++的新手,我无法显示从输入文件读取到结构数组中的信息。

I have 3 functions. 我有3个功能。 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. 我不确定我的readFile()displayAllStudents()是否损坏。 I am able to output the first line from the file, but the rest is 0. Next my selectStudent() is probably terrible; 我能够从文件中输出第一行,但其余为0。接下来,我的selectStudent()可能很糟糕; 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 . . .

In readFile , when you're reading the contents of your file here, you've assumed that all fields for every Student struct contain data. readFile ,当您在此处读取文件的内容时,已假定每个Student结构的所有字段都包含数据。

        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 . 由于您的输入在TotalFinalGrade列中没有内容,因此当程序达到>> Roster[i].Total时,它实际上是试图读取第二个学生的ID c088802 ,它不是整数值由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. 如果知道输入数据永远不会在TotalFinalGrade列中包含内容,则可以从文件读取循环中删除>> Roster[i].Total >> Roster[i].FinalGrade

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;
    }        
}

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. 我在这里做了两个细微的更改:1)使用std::string作为ID,2)使用std::vector而不是数组。

The std::vector is an excellent data structure to use when reading from files, as it expands as necessary. std::vector是从文件读取时使用的出色数据结构,它会根据需要扩展。 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. 我相信您的问题是将operator>>与字符数组一起使用。 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>> ). 您可能希望将数组增加1以允许终止nul字符(可以由operator>>附加)。 The C-style strings (arrays of characters) are terminated by a nul character so you will always need that extra array slot. C样式的字符串(字符数组)以nul字符终止,因此您将始终需要该额外的数组插槽。

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 . 我将所有学生数据方法打包在一个struct Student All the integer data are stored in one std::vector and can be accessed via an index. 所有整数数据都存储在一个std::vector并且可以通过索引进行访问。 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. 请注意:这样,通过简单地使用vectors范围构造函数,将所有学生数据读入vector都是一种方式。

Also the output of all data is an ultra simple one-liner. 同样,所有数据的输出都是非常简单的单线。

For the fun of it, I added also the printSelectedStudendData function. 出于乐趣,我还添加the printSelectedStudendData函数。

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 . 我希望这有帮助 。 . .

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM