void write_file(string db_file) { string tmp; ...
Creado el: 22 de mayo de 2025
Creado el: 22 de mayo de 2025
void write_file(string db_file)
{
string tmp;
/临时存储,内存中构建完整数据库/
for (const auto &db : database)
{
/使用const引用,避免拷贝性能损耗且更安全/
tmp += "DB{\n";
tmp += "name:" + db.name + "\n";
for (const auto &table : db.tables)
{
tmp += "table{\n";
tmp += "name:" + table.name + "\n";
for (const auto &column : table.columns)
{
tmp += "column{\n";
tmp += "name:" + column.name + "\n";
tmp += "type:" + column.type + "\n";
tmp += "value{\n";
for (const auto &val : column.values)
{
tmp = tmp + to_string(val) + "\n";
/to_string定义在my_db.cpp中/
}
tmp += "}\n"; // value
tmp += "}\n"; // column
}
tmp += "}\n";
}
tmp += "}\n";
}
/C++文件写入/
std::ofstream out(db_file);
out << tmp;
out.close();
}#ifndef MY_DB_H
#define MY_DB_H
#include "common.h"
#include <variant>
/C++ 17通用容器/
using ValueType = std::variant<int, float,double,string>;
typedef struct
{
/字段下最多1000个数据/
string name;
string type;
vector<ValueType> values;
}column;
typedef struct
{
string name;
/最多10个字段/
vector<column> columns;
}table;
typedef struct
{
string name;
vector<table> tables;
}db;
extern vector<db> database;
string to_string(const ValueType& value);
#endif
如何实现读取函数进行解析?
下面给出一种尽量简单直观的思路和完整示例代码,帮你把 write_file 输出的文本重新解析回
vector<db> database。核心做法是:
DB › table › column › value。Value 时依据已知 column.type 决定把读到的字符串转成 int | float | double | string 并放进 column.values。my_db.h)几乎不变,只增加读取接口cpp#ifndef MY_DB_H #define MY_DB_H #include "common.h" #include <variant> #include <stack> #include <fstream> #include <sstream> using ValueType = std::variant<int, float, double, std::string>; struct column { std::string name; std::string type; std::vector<ValueType> values; }; struct table { std::string name; std::vector<column> columns; }; struct db { std::string name; std::vector<table> tables; }; extern std::vector<db> database; std::string to_string(const ValueType& value); // 你已有 void read_file(const std::string& db_file); // ← 新增 #endif
my_db.cpp)cpp#include "my_db.h" std::vector<db> database; // 全局实例 /** 将字符串根据 column.type → ValueType */ static ValueType parse_value(const std::string& raw, const std::string& type) { if (type == "int") return std::stoi(raw); if (type == "float") return std::stof(raw); if (type == "double") return std::stod(raw); /* 默认按 string 处理 */ return raw; } /** 去掉行首尾空白 */ static std::string trim(std::string s) { const char* ws = " \t\r\n"; s.erase(0, s.find_first_not_of(ws)); s.erase(s.find_last_not_of(ws) + 1); return s; } void read_file(const std::string& db_file) { std::ifstream in(db_file); if (!in) { throw std::runtime_error("cannot open " + db_file); } database.clear(); std::stack<std::string> ctx; // 记录当前所处层级 db cur_db; table cur_table; column cur_col; std::string line; while (std::getline(in, line)) { line = trim(line); if (line.empty()) continue; // ==== 开启一个块 ==================================================== if (line == "DB{") { ctx.push("DB"); cur_db = db{}; continue; } if (line == "table{") { ctx.push("table"); cur_table = table{}; continue; } if (line == "column{") { ctx.push("column"); cur_col = column{}; continue; } if (line == "value{") { ctx.push("value"); continue; } // ==== 关闭一个块 (“}”) ============================================== if (line == "}") { if (ctx.empty()) throw std::runtime_error("unmatched }"); auto top = ctx.top(); ctx.pop(); if (top == "value") { /* nothing to flush */ } else if (top == "column") { cur_table.columns.push_back(std::move(cur_col)); } else if (top == "table") { cur_db.tables.push_back(std::move(cur_table)); } else if (top == "DB") { database.push_back(std::move(cur_db)); } continue; } // ==== 普通键值 “key:val” ============================================= auto pos = line.find(':'); if (pos == std::string::npos) throw std::runtime_error("invalid line: " + line); std::string key = trim(line.substr(0, pos)); std::string val = trim(line.substr(pos + 1)); const std::string& scope = ctx.top(); if (scope == "DB") { if (key == "name") cur_db.name = val; } else if (scope == "table") { if (key == "name") cur_table.name = val; } else if (scope == "column") { if (key == "name") cur_col.name = val; else if (key == "type") cur_col.type = val; } else if (scope == "value") { // 此时 key 其实就是整个 value 行(因为 write_file 里写的只有数字) /* line 里并无 key:value 形式,所以用完整行作为 val */ ValueType v = parse_value(line, cur_col.type); cur_col.values.push_back(std::move(v)); } } if (!ctx.empty()) throw std::runtime_error("file ended before all blocks closed"); }
说明要点
| 重点 | 解释 |
|---|---|
| 状态机 | 利用 stack<string> 判断当前层级,读到 } 时把本层对象写回父层。 |
| 键值解析 | 行内含 : 认为是 “key:value”;当处于 value{ … } 块时,write_file 里是直接写值,所以直接把整行视为数值。 |
| 类型转换 | 依赖 column.type,在 value 块里读到数据立刻转换为 ValueType。 |
| 错误检查 | 检查括号匹配、文件无法打开、行格式错误等。 |
cppint main() { try { read_file("mydb.txt"); // 反序列化 /* 现在 database 已经填充 */ std::cout << "Loaded " << database.size() << " DB(s)\n"; } catch (const std::exception& ex) { std::cerr << "Error: " << ex.what() << '\n'; } }
string 含冒号或换行)可再做完善。std::getline 配合 reserve 预留内存;或用更高效的解析库(如 boost::spirit)。以上代码能直接编译运行(C++17),足够支撑基本的“写—读—再写”往返流程。祝调试顺利!