Есть ряд областей, в которых вещи усложняются для вас самих, но самая большая проблема связана с вашей попыткой ввода.Ваш ввод гарантированно потерпит неудачу, когда вы берете ввод с помощью:
while(!inputFile.eof())
{
Student st;
inputFile >> st.ID;
inputFile >> st.Name;
inputFile >> st.Course;
inputFile >> st.Credit;
inputFile >> st.Score;
studArr[n] = st;
n++;
}
См .: Почему! .Eof () внутри условия цикла всегда неверно. .По сути, после того, как вы прочитали последнее значение VALID inputFile >> st.Score;
, eofbit
НЕ установлено, поэтому вы повторяете цикл снова, где inputFile >> st.ID;
не удается, оставляя все значения в st
неопределенными, вы не проверяете результат каждого ввода и, следовательно,Вы назначаете studArr[n] = st;
повреждение вашей studArr
.
Один из способов избежать попадания в эту ловушку - это написать перегрузку >>
для обработки ввода для вашей структуры.В этом случае это довольно легко сделать:
struct Student {
int ID = -1;
std::string Name = "";
std::string Course = "";
int Credit = -1;
int Score = -1;
/* overloading the >> and << to read and write your struct helps */
friend std::istream& operator >> (std::istream& is, Student& s) {
is >> s.ID >> s.Name >> s.Course >> s.Credit >> s.Score;
return is;
}
...
};
Теперь чтение и проверка всех вводимых данных становится простым:
#define MAXS 99 /* if you need a constant, #define one (or more) */
...
int main (int argc, char **argv) {
Student studArr[MAXS], tmp;
size_t n = 0;
std::ifstream f(argc > 1 ? argv[1] : "dat/StudentRecords.txt");
while (n < MAXS && f >> tmp)
studArr[n++] = tmp;
...
Если, как и было рекомендовано, вы использовали контейнеры STL, такие каккак std::vector
для хранилища вашего учащегося вместо базового типа массива, проверка будет дополнительно упрощена до while (f >> tmp)
(вы также будете использовать другой метод, например .push_back(tmp)
вместо прямого назначения)
сортировка входных данных, в то время как вы можете сортировать массив, в этом нет необходимости.Простой метод состоит в том, чтобы циклически перебирать данные вашего ученика и собирать уникальное значение ID
в другом массиве (или векторе и т. Д.). Тогда вам нужно только зациклить уникальные идентификаторы в качестве внешнего цикла и перебрать элементы ученика.массив в качестве внутреннего цикла и обработки каждого студента, где идентификатор соответствует текущему значению внешнего цикла.Переписать логику сопоставления вашего GPA и функцию для циклического перебора массива вашего студента, выполняя то, что было описано, можно сделать следующим образом:
/* simple map GPA function that returns grade points given score */
int mapgpa (int score)
{
if (score < 60)
return 0;
else if (score < 70)
return 1;
else if (score < 80)
return 2;
else if (score < 90)
return 3;
else if (score >= 90)
return 4;
return 0;
}
И логику для нахождения GPA для каждого студента:
/* a function to handle outputting the student grades
* in your desired format.
*/
void dogrades (Student *s, size_t n)
{
int uniqueID[MAXS] = {0}; /* array to hold unique IDs */
size_t seen = 0; /* number of unique IDs seen */
for (size_t i = 0; i < n; i++) { /* loop collecting unique IDs */
for (size_t j = 0; j < seen; j++)
if (uniqueID[j] == s[i].ID)
goto next;
uniqueID[seen++] = s[i].ID;
next:;
}
for (size_t j = 0; j < seen; j++) { /* loop ever unique IDs */
int heading = 0, /* simple flag indicating name printed */
sumcredits = 0, /* variable to hold sum of credits */
sumpoints = 0; /* variable to hold sum of gradepoints */
for (size_t i = 0; i < n; i++) { /* loop over each stuct element */
int gradepts = 0; /* var to map score->gradepoits */
if (s[i].ID == uniqueID[j]) { /* if struct element == ID */
if (!heading) { /* if no heading output, do it */
std::cout << s[i].ID << " " << s[i].Name << "\n\n";
heading = 1; /* set flag indicating done */
}
gradepts = mapgpa(s[i].Score); /* get gradepts from score */
/* output current course and gradepoints */
std::cout << s[i].Course << " "
<< s[i].Credit << " "
<< s[i].Score << " "
<< gradepts << ".0\n\n";
sumcredits += s[i].Credit; /* sum credits */
sumpoints += s[i].Credit * gradepts; /* sum gradepoints */
}
}
std::cout.precision(3); /* set precsion and output GPA */
std::cout << "======================\n\n"
<< "GPA " << (double)sumpoints/sumcredits << "\n\n"
<< "======================\n\n";
}
}
С вашей логикой, обработанной в функции dogrades
, ваша main()
уменьшается до:
int main (int argc, char **argv) {
Student studArr[MAXS], tmp;
size_t n = 0;
std::ifstream f(argc > 1 ? argv[1] : "dat/StudentRecords.txt");
while (n < MAXS && f >> tmp)
studArr[n++] = tmp;
dogrades (studArr, n);
}
В целом, в примере, который вы можете скомпилировать, который принимает имя файла данных для чтения какпервый аргумент (или читает по умолчанию "dat/StudentRecords.txt"
), вы можете сделать следующее:
#include <iostream>
#include <iomanip>
#include <fstream>
#define MAXS 99 /* if you need a constant, #define one (or more) */
struct Student {
int ID = -1;
std::string Name = "";
std::string Course = "";
int Credit = -1;
int Score = -1;
/* overloading the >> and << to read and write your struct helps */
friend std::istream& operator >> (std::istream& is, Student& s) {
is >> s.ID >> s.Name >> s.Course >> s.Credit >> s.Score;
return is;
}
friend std::ostream& operator << (std::ostream& os, const Student& s) {
os << std::setw(5) << s.ID << " "
<< std::setw(8) << std::left << s.Name << " "
<< std::setw(5) << s.Course << " "
<< s.Credit << " "
<< std::setw(3) << s.Score << '\n';
return os;
}
};
/* simple map GPA function that returns grade points given score */
int mapgpa (int score)
{
if (score < 60)
return 0;
else if (score < 70)
return 1;
else if (score < 80)
return 2;
else if (score < 90)
return 3;
else if (score >= 90)
return 4;
return 0;
}
/* a function to handle outputting the student grades
* in your desired format.
*/
void dogrades (Student *s, size_t n)
{
int uniqueID[MAXS] = {0}; /* array to hold unique IDs */
size_t seen = 0; /* number of unique IDs seen */
for (size_t i = 0; i < n; i++) { /* loop collecting unique IDs */
for (size_t j = 0; j < seen; j++)
if (uniqueID[j] == s[i].ID)
goto next;
uniqueID[seen++] = s[i].ID;
next:;
}
for (size_t j = 0; j < seen; j++) { /* loop ever unique IDs */
int heading = 0, /* simple flag indicating name printed */
sumcredits = 0, /* variable to hold sum of credits */
sumpoints = 0; /* variable to hold sum of gradepoints */
for (size_t i = 0; i < n; i++) { /* loop over each stuct element */
int gradepts = 0; /* var to map score->gradepoits */
if (s[i].ID == uniqueID[j]) { /* if struct element == ID */
if (!heading) { /* if no heading output, do it */
std::cout << s[i].ID << " " << s[i].Name << "\n\n";
heading = 1; /* set flag indicating done */
}
gradepts = mapgpa(s[i].Score); /* get gradepts from score */
/* output current course and gradepoints */
std::cout << s[i].Course << " "
<< s[i].Credit << " "
<< s[i].Score << " "
<< gradepts << ".0\n\n";
sumcredits += s[i].Credit; /* sum credits */
sumpoints += s[i].Credit * gradepts; /* sum gradepoints */
}
}
std::cout.precision(3); /* set precsion and output GPA */
std::cout << "======================\n\n"
<< "GPA " << (double)sumpoints/sumcredits << "\n\n"
<< "======================\n\n";
}
}
int main (int argc, char **argv) {
Student studArr[MAXS], tmp;
size_t n = 0;
std::ifstream f(argc > 1 ? argv[1] : "dat/StudentRecords.txt");
while (n < MAXS && f >> tmp)
studArr[n++] = tmp;
dogrades (studArr, n);
}
( note: перенаправление оператора <<
также было добавлено вупростите вывод необработанной информации о структуре, если вам потребуется)
Пример использования / Вывод
$ ./bin/studentrecords
12546 Amy
CS1 4 81 3.0
CS2 4 90 4.0
CS3 3 90 4.0
======================
GPA 3.64
======================
13455 Bill
CS1 4 76 2.0
CS2 4 85 3.0
CS3 3 75 2.0
======================
GPA 2.36
======================
14328 Jim
CS1 4 64 1.0
CS2 4 71 2.0
CS3 3 69 1.0
======================
GPA 1.36
======================
14388 Henry
CS3 3 80 3.0
======================
GPA 3
======================
15667 Peter
CS3 3 45 0.0
======================
GPA 0
======================
Просмотрите все и дайте мне знать, если у вас есть вопросы (и серьезно подумайте об использовании контейнеров STL, таких как std::vector
и std::map
)