用文本格式(key=value)存kv对最稳,避免二进制兼容性问题;必须全量读写、用std::ios::trunc清空文件;仅限单线程低频场景,不支持并发、原子操作或软删除。

用 std::fstream 直接序列化 KV 对,别碰二进制格式细节
直接用文本格式存键值对最稳,避免字节序、对齐、类型尺寸变化带来的读写错位。C++ 没有内置的序列化标准,自己搞二进制结构体(比如 struct Entry { char key[64]; int value; };)在跨平台或升级编译器后极大概率读崩。
实操建议:
- 每行存一个
key=value,用std::getline逐行读,std::string::find('=')分割 - 写入时统一用
\n换行(Windows 下用std::ios::binary打开也能兼容,但没必要) - 键里不能含
=或换行符;值可含等号,但需约定只取第一个=左侧为键 —— 所以实际应限制键字符集(如仅a-zA-Z0-9_) - 不支持空键、空值;遇到就跳过或报错,别静默忽略
插入/更新必须重写整个文件,别试图 seekp 随机改
文本格式无法原地覆盖:旧键值长度和新键值长度不同,seekp 后写入会把后续内容挤掉或留下脏字节。哪怕只改一个值,也要全量读入内存、修改映射、再全量写出。
常见错误现象:
- 文件末尾出现乱码或重复键 —— 是部分写入没清空原文件导致的
- 读出的值比写入的小 ——
seekp后没调file.clear(),流状态出错 - Windows 下用
"w"模式打开却仍残留旧内容 —— 没用std::ios::trunc标志
正确做法:
- 读阶段:用
std::ifstream打开,while (getline(...))构建std::unordered_map<:string std::string></:string> - 写阶段:用
std::ofstream file("db.txt", std::ios::out | std::ios::trunc),确保旧内容被清空 - 写完立刻
file.close(),别依赖析构 —— 出异常时可能丢数据
并发写入必然丢数据,单线程是唯一安全模式
C++ 标准库的 std::fstream 不提供文件锁,多个进程或线程同时写同一个文件,结果取决于操作系统调度,不是“最后写的赢”,而是“谁写到哪算哪”。没有原子性保证,也没有事务回滚。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
使用场景限制很明确:
- 只适用于单进程内、单线程调用的配置缓存、调试日志开关等低频场景
- 绝不能用于 Web 服务后台、多 worker 进程共享存储
- 如果真要多线程访问,必须外加互斥锁(
std::mutex),且锁粒度得包住「读 → 改 → 写」整段,不能只锁写入
性能影响:
- 1000 条记录,每次写入约耗时 1–5ms(SSD 上),纯文本解析+重建 map 是主要开销
- 文件超过 10MB 后,启动加载变慢,但写入时间不线性增长 —— 因为写入仍是全量刷盘
删除键 = 内存中删掉再全量重写,不存在“标记删除”
文本格式没法打标记,也没法收缩文件空洞。所谓“软删除”在这里毫无意义,只会让文件越积越大、读取越慢、还增加解析逻辑复杂度。
实操要点:
- 删除操作就是从
std::unordered_map中调用.erase(key) - 随后必须触发一次完整写入,否则下次启动仍会读到旧值
- 不要为了“节省 IO”而延迟写入 —— 崩溃或 kill -9 会导致最新状态丢失
- 如果担心频繁写入损耗 SSD,那就别用这个方案;该上
sqlite3或leveldb
容易被忽略的是:没有“存在性检查”的原子操作。比如 if (!exists(key)) insert(key, val); 这种逻辑,在多线程下天然竞态 —— 判断和插入之间可能已被其他线程插入。这种需求已超出本方案能力边界。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!









