C++学生信息和考试成绩管理系统
第四章 考试创建&成绩的增改
一、考试创建
下面我们具体说一下,考试的创建和成绩查询、修改。我写了显示可以选择创建两个考试,实际上只能创建一种,另一种没写。看看下面这些代码,每一片代码的作用都写了,其实就是不停地输入、获取,最后调用自定义函数存储到文件中,再给配置文件的[record]增加记录即可。
当前位置:manage_admin.cpp
void print_menu_admin_exam_create_comprehensive_examination()
{
while (true)
{
CLEAN;
// ---------- Start ----------
switch (input_judgment(true, true, "Do you want to create comprehensive examination? (Y/N) "))
{
case -1:
continue;
case 0:
return;
case 1:
default:
break;
}
// 创建考试类对象的操作类
auto* operation = new operation_info<exam_info>;
// 日期时间 (Date-time)
operation->info_class->date_time = input_datetime();
// 自动添加创建者 (Creator or administrator)
char* tmp_admin = new char[strlen(g_vector_login_info[0]) + 1];
strcpy_s(tmp_admin, strlen(g_vector_login_info[0]) + 1, g_vector_login_info[0]);
operation->info_class->admin = tmp_admin;
// 考生人数 (Count Students)
while (true)
{
std::string tmp = input(3, true, "Please input amount of students: ");
if (is_positive_integer(tmp.c_str()))
{
operation->info_class->count_student = strtol(tmp.c_str(), nullptr, 0L);
break;
}
print_wait("Please input correctly.");
}
// 应考科目数 (Count majors)
while (true)
{
std::string tmp = input(2, true, "Please input amounts of subject: ");
if (is_positive_integer(tmp.c_str()))
{
operation->info_class->count_subject = strtol(tmp.c_str(), nullptr, 0L);
break;
}
print_wait("Please input amounts of major.");
}
// 科目编号 (Major serial number)
for (int i = 0; i < operation->info_class->count_subject; i++)
{
std::cout << "Please set the " << i + 1 << " serial id/name of subject: ";
operation->info_class->vector_subject_serial_number.emplace_back(input(4));
}
// 考生记录 (Examination records of student)
for (int i = 0; i < operation->info_class->count_student; i++)
{
auto* tmp_student_exam_record = new exam_record_info;
while (true)
{
std::cout << "Please input the " << i + 1 << " student id: ";
char* input_student_id = input(MAXSIZE_STUDENT_ID);
auto* tmp_operation = user_select(input_student_id, 2);
if (tmp_operation->info_flag)
{
tmp_student_exam_record->student_name = new char[strlen(tmp_operation->info_class->name) + 1];
strcpy_s(tmp_student_exam_record->student_name, strlen(tmp_operation->info_class->name) + 1, tmp_operation->info_class->name);
tmp_student_exam_record->student_id = input_student_id;
tmp_student_exam_record->class_id = new char[strlen(tmp_operation->info_class->class_id) + 1];
strcpy_s(tmp_student_exam_record->class_id, strlen(tmp_operation->info_class->class_id) + 1, tmp_operation->info_class->class_id);
free_ptr(tmp_operation);
break;
}
print_wait("Not found user. Please try input again.");
}
// 循环录入每科成绩 (Recycle entry of each subject score)
for (int j = 0; j < operation->info_class->count_subject; j++)
{
std::cout << "Please input the score of \"" << operation->info_class->vector_subject_serial_number[j] << "\": ";
double tmp_score = 0;
while (true)
{
char* tmp = input(MAXSIZE_BUFF);
if (std::regex_match(tmp, std::regex("^([1-9][0-9]*)+(\\.[0-9]{1,2})?$")))
{
tmp_score = strtod(tmp, nullptr);
free_ptr(tmp, true);
break;
}
print_wait("Please input correct number!");
}
tmp_student_exam_record->score_average += tmp_score;
tmp_student_exam_record->vector_score_subject.emplace_back(tmp_score);
if (0 == i)
{
operation->info_class->vector_subject_average_score.emplace_back(tmp_score);
}
else
{
operation->info_class->vector_subject_average_score[j] += tmp_score;
}
}
if (0 != operation->info_class->count_student && 0 != operation->info_class->count_subject)
{
// 自动统计单个学生平均总分 (Calculate the average total score of each student)
tmp_student_exam_record->score_average /= operation->info_class->count_subject;
// 自动累加全部学生总分 (Add up the total score of all students)
operation->info_class->average_score += tmp_student_exam_record->score_average;
}
// 存入考试记录对象 (Save the test record object)
operation->info_class->vector_student_exam_record.emplace_back(tmp_student_exam_record);
}
// 自动计算全部考生总平均成绩 (Calculate the average score of all candidates)
operation->info_class->average_score /= operation->info_class->count_student;
// 自动计算每科平均成绩 (Calculate the average grade of each subject)
for (int i = 0; i < operation->info_class->count_subject; i++)
{
operation->info_class->vector_subject_average_score[i] /= operation->info_class->count_student;
}
// 自动排名 (Rankings)
std::sort(operation->info_class->vector_student_exam_record.begin(), operation->info_class->vector_student_exam_record.end(), sort_student_exam_record);
for (int i = 0; i < operation->info_class->count_student; i++)
{
if (!operation->info_class->vector_student_exam_record.empty())
{
operation->info_class->vector_student_exam_record[i]->rankings = i + 1;
}
}
// ---------- Reconfirm ----------
switch (input_judgment(true, true, "Are you confirm finish? (Y/N) "))
{
case 0:
print_wait("Cancelled.");
case -1:
return;
case 1:
default:
break;
}
// ---------- Store ----------
if (!exam_store(operation->info_class)) // 自定义函数:存储考试记录
{
free_ptr(operation);
print_wait("Save exam record failed.");
print_log("Failed to create exam. ", severity_code_error);
continue;
}
// ---------- Finish ----------
CLEAN;
operation->info_class->print_exam();
free_ptr(operation);
print_wait("Create examination successfully!");
print_log("Create comprehensive examination successfully.", severity_code_info);
}
}
上面的代码中有个自定义的存储函数exam_store()
,它的作用就是把新创建的考试类对象,按格式存储到./data/record/
下,然后将init.ini
里的[record]
增加新的考试记录文件所在位置和文件名。
当前位置:process_file.cpp
bool exam_store(const exam_info* info, char* path)
{
std::fstream f;
std::string tmp_path = PATH_FOLDER_RECORD"exam_*****.csv";
try
{
if (nullptr == path)
{
if (g_vector_file_path_record.empty())
{
print_log("Create an record file, because not record files exist", severity_code_info);
f.open(PATH_FOLDER_RECORD"exam_0.csv", std::ios::out | std::ios::trunc | std::ios::binary);
}
else
{
std::vector<int> tmp_vector_index;
for (const auto& tmp_exam_path : g_vector_file_path_record)
{
auto tmp_trim_path = trim(tmp_exam_path);
const std::string::size_type idx_head = tmp_trim_path.find("exam_");
const std::string::size_type idx_tail = tmp_trim_path.find(".csv");
if (idx_head == std::string::npos || idx_tail == std::string::npos) {
std::cout << "None" << std::endl;
continue;;
}
const unsigned int length = static_cast<const unsigned int>(idx_tail) - static_cast<const unsigned int>(idx_head) - 4;
tmp_vector_index.emplace_back(strtol(tmp_trim_path.substr(idx_head + 5, length - 1).c_str(), nullptr, 0L));
}
if (!tmp_vector_index.empty())
{
std::sort(tmp_vector_index.begin(), tmp_vector_index.end(), std::less<>());
const std::string::size_type idx_head = tmp_path.find("exam_");
const std::string::size_type idx_tail = tmp_path.find(".csv");
const unsigned int length = static_cast<const unsigned int>(idx_tail) - static_cast<const unsigned int>(idx_head) - 4;
char tmp_digits[255];
sprintf_s(tmp_digits, sizeof(tmp_digits), "%d", tmp_vector_index[tmp_vector_index.size() - 1] + 1);
tmp_path.replace(idx_head + 5, length - 1, tmp_digits);
}
f.open(tmp_path.c_str(), std::ios::out | std::ios::trunc | std::ios::binary);
}
}
else
{
f.open(path, std::ios::out | std::ios::trunc | std::ios::binary);
}
if (!f.is_open())
{
return false;
}
f << info->date_time << "," << info->admin << "," << info->count_student << "," << info->average_score << "," << info->count_subject << "\r\n";
if (!info->vector_subject_serial_number.empty())
{
for (const auto* tmp : info->vector_subject_serial_number)
{
f << tmp;
if (0 != strcmp(tmp, info->vector_subject_serial_number.back()))
{
f << ",";
}
else
{
f << "\r\n";
}
}
}
if (!info->vector_subject_average_score.empty())
{
for (unsigned int i = 0; i < info->vector_subject_average_score.size(); i++)
{
f << info->vector_subject_average_score[i];
if (i != info->vector_subject_average_score.size() - 1)
{
f << ",";
}
else
{
f << "\r\n";
}
}
}
if (!info->vector_student_exam_record.empty())
{
for (const auto& tmp : info->vector_student_exam_record)
{
f << tmp->rankings << ",";
f << tmp->student_name << ",";
f << tmp->class_id << ",";
f << tmp->student_id << ",";
f << tmp->score_average << ",";
for (unsigned int i = 0; i < tmp->vector_score_subject.size(); i++)
{
f << tmp->vector_score_subject[i];
if (i != tmp->vector_score_subject.size() - 1)
{
f << ",";
}
else
{
f << "\r\n";
}
}
}
}
f.close();
if (!properties_update(3, tmp_path.c_str()))
{
return false;
}
}
catch (const std::exception& e)
{
print_log(e.what(), severity_code_error);
return false;
}
return true;
}
二、更改成绩
至此,考试的创建代码已经完成了,下面简要说一下更改成绩,本质上也是遍历全部考试成绩记录,逐行读取学生类对象,找到对应学号,将这个页面文件装到容器里,修改值,再清空这个文件,并将这个容器写回文件,完成成绩的修改。
下面这个是交互界面,也就是选择更新成绩后跳转到的页面代码。
当前位置:manage_admin.cpp
void print_menu_admin_exam_update(menu& menus)
{
while (true)
{
CLEAN;
// ---------- Start ----------
switch (input_judgment(true, true, "Do you want to update scores of student? (Y/N)"))
{
case -1:
continue;
case 0:
return;
case 1:
default:
break;
}
char* student_id;
char* datetime_start;
char* datetime_end;
operation_info<exam_info*>* operation;
while (true)
{
// ---------- Input Student Id ----------
while (true)
{
student_id = input(MAXSIZE_STUDENT_ID, true, "Please input Student Id: ");
if (std::regex_match(student_id, std::regex("^[0-9]\\d*$")))
{
break;
}
print_wait("Illegally student id input!");
free_ptr(student_id, true);
}
// ---------- Input start datetime of exam ----------
while (true)
{
datetime_start = input(MAXSIZE_DATETIME_LENGTH, true, "Please input start datetime: ");
if (0 == strcmp(trim(datetime_start).c_str(), "all"))
{
break;
}
if (MAXSIZE_DATETIME_LENGTH == strlen(datetime_start) && std::regex_match(datetime_start, std::regex("^[1-9]\\d*$")))
{
break;
}
print_wait("Illegally start datetime input!");
free_ptr(datetime_start, true);
}
// ---------- Input end datetime of exam ----------
while (true)
{
datetime_end = input(MAXSIZE_DATETIME_LENGTH, true, "Please input end datetime: ");
if (0 == strcmp(trim(datetime_end).c_str(), "all"))
{
break;
}
if (MAXSIZE_DATETIME_LENGTH == strlen(datetime_end) && std::regex_match(datetime_end, std::regex("^[1-9]\\d*$")))
{
break;
}
print_wait("Illegally end datetime input!");
free_ptr(datetime_end, true);
}
// ---------- Select ----------
operation = exam_select(const_cast<const char*>(student_id), datetime_start, datetime_end);
if (!operation->info_vector_class.empty())
{
break;
}
free_ptr(datetime_end, true);
free_ptr(datetime_start, true);
free_ptr(student_id, true);
free_ptr(operation);
std::cout << "Not found user." << std::endl;
switch (input_judgment(true, true, "Do you want to try again? (Y/N)"))
{
case -1:
print_wait("Illegally input, cancelled.");
return;
case 0:
print_wait("Cancelled.");
return;
case 1:
default:
break;
}
}
// ---------- Print Old Exam ----------
if (1 == input_judgment(true, true, "Print out all exam? (Y/N)"))
{
for (const auto& tmp : operation->info_vector_class)
{
tmp->print_exam();
printf_s("\n\n");
}
}
char* subject_serial_id;
char* student_new_score;
while (true)
{
// ---------- Input Subject Name ----------
subject_serial_id = input(6, true, "Please input subject serial id: ");
// ---------- Input New Score ----------
while (true)
{
student_new_score = input(6, true, "Please input new score: ");
if (std::regex_match(student_new_score, std::regex("^[0-9]+(\\.[0-9]{2})?$")))
{
break;
}
print_wait("Illegally input!");
free_ptr(student_new_score, true);
}
operation = exam_update(operation, student_id, subject_serial_id, strtod(student_new_score, nullptr));
if (operation->info_flag)
{
break;
}
print_wait("Not found or Cannot update score.");
free_ptr(student_new_score, true);
free_ptr(subject_serial_id, true);
}
int count_alter_success_file = 0;
// ---------- Store ----------
for (unsigned int i = 0; i < operation->info_vector_class.size(); i++)
{
try
{
if (exam_store(operation->info_vector_class[i], operation->info_vector_path[i]))
{
count_alter_success_file++;
}
}
catch (...)
{
print_log("Error to save alter file.");
}
}
// ---------- Print New Exam ----------
if (1 == input_judgment(true, true, "Print out all exam? (Y/N)"))
{
for (const auto& tmp : operation->info_vector_class)
{
tmp->print_exam();
printf_s("\n\n");
}
}
std::cout << "Student ID: " << student_id << std::endl;
std::cout << "Subject: " << subject_serial_id << std::endl;
std::cout << "New Score: " << student_new_score << std::endl;
std::cout << "All files: " << operation->info_vector_class.size() << std::endl;
std::cout << "Success: " << count_alter_success_file << "\t" << "Failed: " << operation->info_vector_class.size() - count_alter_success_file << std::endl;
print_wait("Operation success!");
free_ptr(student_id, true);
free_ptr(student_new_score, true);
free_ptr(subject_serial_id, true);
free_ptr(datetime_start, true);
free_ptr(datetime_end, true);
free_ptr(operation);
}
}
要讲的其实并不多,主旨也就那样,函数也都介绍了。注意下写的注释,根据注释猜测应该能猜出来个大概,这个区域要做什么,再根据变量名去猜测,这个变量是做什么的,英文可不能白学。至于常量,我基本是定义在头文件里,根据名字去猜它是做什么的。