必须约定每条记录独占一行且key/value禁止换行(或转义),写入后强制flush();否则getline会吞行、漏读、卡死或丢失数据。

为什么不能直接用 std::ofstream 一行写一个 key=value 就完事?
能写进去,但读不出来——或者读错、漏读、卡死。根本原因是没处理好“记录边界”。文本格式的 KV 如果不加换行或分隔符,std::getline 会把多个键值对吞成一行;如果用了换行但 key 或 value 本身含换行(比如存 JSON),又会彻底破坏结构。更隐蔽的问题是:文件末尾缺换行符时,最后一行可能被 std::getline 忽略(取决于实现和状态位)。
- 必须约定每条记录独占一行,且
key和value内部禁止换行(或提前转义) - 写入后强制调用
flush(),否则进程崩溃时最后几条可能丢失 - 避免用
operator 直接输出裸字符串——它不保证原子性,多线程写入会交错
怎么安全地追加一条 key=value 记录到文件?
核心是“打开即写,写完即关”,绕过缓存和并发干扰。不要长期持有 std::fstream 对象,尤其在多线程环境。
- 每次写都用
std::ofstream以std::ios::app模式打开,它自带原子性追加语义(POSIX 层面) - 先拼好整行:
line = key + "=" + value,再一次性写入line + "\n" - 检查
is_open()和good(),写失败时别静默吞掉错误(比如磁盘满) - 示例:
void put(const std::string& key, const std::string& value) { std::ofstream f("db.txt", std::ios::app); if (!f.is_open()) return; f
读取时为什么 find() 总是返回空?
因为没做全量扫描——你不能指望 std::map 那种 O(1) 查找。这个“无索引”数据库本质是顺序扫描文件,每次 get(key) 都得从头读到尾。
- 用
std::ifstream打开文件,循环调用std::getline逐行读 - 每行用
find("=")分割,但注意:key 里不能含=,否则解析错位 - 匹配成功后立刻
break,别继续读下去(虽然文件小,但习惯要养) - 记得处理空行、注释行(如以
#开头)——否则可能把垃圾当数据
删除某条 key 时,为什么不能只“覆盖那一行”?
文件不是内存数组,没有“随机擦除”操作。硬盘上删中间一行,等于要把后面所有内容往前搬——这代价远超重写整个文件。
- 安全做法:读出全部有效记录到内存(
std::vector<:pair std::string>>),过滤掉要删的 key,再全量重写文件 - 重写前先写临时文件(如
db.txt.tmp),写完fsync()再rename()替换原文件,防止中断导致库损坏 - 如果数据量超过几十 MB,这种方案就明显卡顿——这就是“无索引”的硬伤,别硬撑
真正容易被忽略的是:没有事务、没有锁、没有校验。两个进程同时写,大概率得到半截损坏的文件;value 里有不可见控制字符,解析时会静默截断。所谓“最简易”,其实是把复杂性推给了使用者——你得自己管好并发、编码、边界。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











