T
You can read fast, but I'll offer you another option - to count it in the buffer and dissolve it. It is desirable to create an object that runs a list of students. Well, here's an example: #include <cstdio>
#include <string>
#include <vector>
#include <map>
struct Student{
std::string Group, Surname;
std::map<std::string, int> subjects;
float Average(){ // Зачем хранить Average как отдельную переменную?
float accum = 0;
for(auto si = subjects.begin(); si != subjects.end(); ++si) accum += si->second;
return accum / subjects.size();
}
};
class StudentsManager{
std::vector<Student> students;
std::vector<std::string> subjects;
std::string GetValue(const std::string &source, const std::string &lb, std::string marker){
int begin = source.find(marker);
if (-1 == begin) return "";
begin+= marker.size();
int end = source.find(lb, begin);
if (-1 == end) end = source.size();
return source.substr(begin, end - begin);
}
bool ParseData(const std::string &buffer){ // Передаём ссылку, нам лишняя копия файла не нужна в памяти
std::string obj_delim("*"); // разделитель объектов
std::string linebreak("\n");
// определяем как устроен разрыв сток - по \r\n или \n
// (можно стереть следующие 2 строки, если это заведомо известно)
if (-1 != buffer.find("\r\n")) linebreak = "\r\n";
else if(-1 == buffer.find(linebreak)) return false;
int begin = -1;
while(++begin < buffer.size()){
begin = buffer.find(obj_delim, begin);
if (-1 == begin) break;
int end = buffer.find(obj_delim, ++begin);
if (-1 == end) break;
std::string obj_buf = buffer.substr(begin,end);
Student student;
student.Group = GetValue(obj_buf, linebreak, "Group:");
student.Surname = GetValue(obj_buf, linebreak, "Surname:");
if (!student.Group.empty() && !student.Surname.empty()){
for(auto si = subjects.begin(); si != subjects.end(); ++si){
auto mark = GetValue(obj_buf, linebreak, *si);
if (mark.empty()) continue;
if (auto val = std::stoi(mark)) student.subjects[*si] = val;
}
students.push_back(student);
}
begin = end;
}
return !students.empty();
}
public:
bool AddSubject(std::string subject){
subject+= ":";
auto si = subjects.begin();
for(;si!= subjects.end(); ++si) if (subject == *si) break;
if (si != subjects.end()) return false; // есть уже такой
subjects.emplace_back(subject);
return true;
}
bool LoadData(const char *filenane){
// Открываем и читаем весь файл в буфер...
// Если не нравится классический метод fopen / fread / fclose, можно переписать на std::ifstream
auto file = fopen(filenane, "rb");
if (!file) return false;
fseek(file, 0, SEEK_END);
std::string buffer(ftell(file),0); // выделяем буфер под файл
rewind(file);
if (buffer.size() != fread(&buffer[0], 1, buffer.size(), file)){
fclose(file);
return false;
}
fclose(file);
return ParseData(buffer);
}
void ShowStudents(){
for(auto si = students.begin(); si != students.end(); ++si){
auto student = &(*si); // работаем с указателем на студента, чтобы не копировать лишний раз
printf("*\nGroup: %s\nSurname: %s", student->Group.c_str(), student->Surname.c_str());
if (!student->subjects.empty()){
for(auto ssi = student->subjects.begin(); ssi != student->subjects.end(); ++ssi)
printf("\n%s %d", ssi->first.c_str(), ssi->second);
printf("\nAverage: %f", student->Average());
}
printf("\n*\n");
}
}
};
int main(int argc, char** argv) {
if (argc < 2) return 1;
StudentsManager smgr;
smgr.AddSubject("CompScience");
smgr.AddSubject("Math");
smgr.AddSubject("Phys");
// Список дисциплин тоже можно читать из файла
smgr.LoadData(argv[1]);
smgr.ShowStudents();
return 0;
}
The rest of the work on entry from consoles, additions, removals, etc. can already be done by analogy