C++ 學生成績管理系統,`Student` 類包含動態數量的 `Course` 對象數組。需將對象持久化到文件,後續讀取還原。
// 文件結構:
// [Student Count: int32]
// 循環 Student Count 次:
// [Student ID: int32] [Name Len: uint16] [Name: char[]] [Course Count: uint16]
// 循環 Course Count 次:
// [Course ID: int32] [Name Len: uint16] [Name: char[]] [Credit: float] [Grade: float]
#include
#include
#include
#include
struct Course {
int32_t id;
std::string name;
float credit;
float grade;
};
struct Student {
int32_t id;
std::string name;
std::vector courses;
};
// 寫入單個字符串:長度 + 內容
void writeString(std::ofstream &ofs, const std::string &s) {
uint16_t len = static_cast(s.size());
ofs.write(reinterpret_cast(&len), sizeof(len));
ofs.write(s.data(), len);
}
// 讀取字符串
std::string readString(std::ifstream &ifs) {
uint16_t len;
ifs.read(reinterpret_cast(&len), sizeof(len));
std::string s(len, '\0');
ifs.read(&s[0], len);
return s;
}
// 序列化 Student
void saveStudents(const std::vector &students, const char *filename) {
std::ofstream ofs(filename, std::ios::binary);
int32_t count = students.size();
ofs.write(reinterpret_cast(&count), sizeof(count));
for (const auto &stu : students) {
ofs.write(reinterpret_cast(&stu.id), sizeof(stu.id));
writeString(ofs, stu.name);
uint16_t ccount = stu.courses.size();
ofs.write(reinterpret_cast(&ccount), sizeof(ccount));
for (const auto &c : stu.courses) {
ofs.write(reinterpret_cast(&c.id), sizeof(c.id));
writeString(ofs, c.name);
ofs.write(reinterpret_cast(&c.credit), sizeof(c.credit));
ofs.write(reinterpret_cast(&c.grade), sizeof(c.grade));
}
}
}
// 反序列化
std::vector loadStudents(const char *filename) {
std::ifstream ifs(filename, std::ios::binary);
std::vector students;
int32_t count;
ifs.read(reinterpret_cast(&count), sizeof(count));
students.reserve(count);
for (int i = 0; i < count; ++i) {
Student stu;
ifs.read(reinterpret_cast(&stu.id), sizeof(stu.id));
stu.name = readString(ifs);
uint16_t ccount;
ifs.read(reinterpret_cast(&ccount), sizeof(ccount));
stu.courses.reserve(ccount);
for (int j = 0; j < ccount; ++j) {
Course c;
ifs.read(reinterpret_cast(&c.id), sizeof(c.id));
c.name = readString(ifs);
ifs.read(reinterpret_cast(&c.credit), sizeof(c.credit));
ifs.read(reinterpret_cast(&c.grade), sizeof(c.grade));
stu.courses.push_back(c);
}
students.push_back(std::move(stu));
}
return students;
}
#include
using json = nlohmann::json;
void saveStudentsJson(const std::vector &students, const char *filename) {
json j = students; // 自動序列化 (需為 struct 定義 to_json/from_json 或用 NLOHMANN_DEFINE_TYPE_INTRUSIVE)
std::ofstream ofs(filename);
ofs << j.dump(4); // 美化縮進
}
std::vector loadStudentsJson(const char *filename) {
std::ifstream ifs(filename);
json j = json::parse(ifs);
return j.get>();
}
// 為 Student/Course 定義序列化 (C++17 結構化綁定)
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Course, id, name, credit, grade)
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Student, id, name, courses)
「輸出流函數 write() 和輸入流函數 read() 可以存對象嗎?」
絕對不能**用於含非 POD 類型 (std::string, std::vector, 虛函數類) 的類**。
最佳回答核心思路:「每個課程類的大小應該是一樣的吧。就像往文件裡寫結構體一樣、一把寫進去,你的程序輸出文件如果是一個學生一個,就可以把這個學生選了幾門課做一個計數、寫在文件開頭,在讀取文件的時候就可以先讀取一個 int,然後再讀取課程信息啦」—— 給出了長度前綴序列化的核心思想。