一.引入
我现在写的这个命令行记账本是一个非常基础,非常小的项目,我写这个项目的主要目的是在我写迷你数据库的项目之前对我的技术栈进行热身和完善,虽然项目不大,但是也踩了不少坑,涉及到的技术栈有c++17、STL库、文件流(fstream)
二.项目功能
写了四个菜单,功能分别是退出程序、记一笔账(存储格式:一行一笔账,\t分隔),查流水、按类别统计
展示如下:
持久化保存的文件里现有的内容:
运行过程如下:
持久化保存的文件里经过如上的内容:
三、坑
| 坑 | 原因;解决方式 |
| 1.cin>> 和 getline 混用,备注被"吃"了 | 残留换行符;用cin.ignore()把输入缓冲区里残留的换行符冲掉 |
| 2.空 vector 用 v[0],程序崩溃 | "未定义行为" |
| 3.往菜单选项里输入字母,程序卡死 | 原本while(true),cin 读取失败后陷入失败状态且未恢复,循环条件永真;改用while(cin>>x)当循环条件,读取失败自动退出循环 |
| 4.double类型数据自己四舍五入并且保留位数还不同 | double 默认 6 位有效数字(不是 6 位小数);利用fixed+setprecision(2) |
| 5.0.1+0.2≠0.3 | 二进制存不下十进制小数;银行用整数"分" |
| 6.中文乱码大战 | UTF-8 vs GBK 密码本;chcp 失败;ConPTY"传话人"(当发现文件编码和chcp已经改成UTF-8但显示还是乱码,这个问题最后我是借助ai辅助解决的);系统级 UTF-8 根治 |
| 7.文件格式契约 | 字段顺序错位;前 3 格按格拆,尾部全部归备注(因为前三个字段固定、备注允许任意内容) |
| 8.把金额改成 abc,程序当场暴毙 | stod 抛异常;try-catch 接住,坏行跳过 |
这里我再重点把坑4、6、8给详细描述一下
坑4的发现是我在给记一笔这个选项增加持久化保存功能时发现的,超过 6 位有效数字的小数会被四舍五入到 6 位有效数字,这就导致不同数字的小数点后几位保留的位数都不一致,为了让这些小数统一保留到小数点后两位,我经过资料查阅利用fixed+setprecision(2)解决了问题
坑6的乱码问题出现的时候,我一开始以为是我文件的编码和终端编码不一致导致的,于是我修改了文件的编码和终端(chcp)的编码,统一为UTF-8但是还是乱码问题。最终经过一些时间的努力,我没有头绪,就借助Claude Code把问题定位到我用的集成工具vscode和终端的中间“传话人”ConPTY上,最后我直接把后台系统的编码改成UTF-8,这个问题就解决了
坑8的问题其实是很常见的问题,具体类型是std::invalid_argument"参数不合法",为了避免“一粒老鼠屎坏了一锅粥”的问题,直接利用try-catch就可以轻松避免这个问题
四、总结
利用这个项目帮我温故而知新一些新知识,包括智能指针、RAII以及简单的try-catch异常处理并且对于以前出现过以及新出现的bug有了经验,在以后再次出现的时候可以更高效更准确地解决问题。但是毫无疑问的是,这个小项目在使用过程中存在一些瓶颈如下:
- 启动慢:文本格式逐行解析是主因 → 换二进制格式(直接 memcpy 结构体,快一个数量级)或建索引文件;
- 查流水慢:printAll 10 万行全打印,瓶颈在屏幕 IO →分页显示;
- 查某天/某类别的账:现在没有索引,得 O(n) 扫 → 加索引或排序;
- 如果数据量再大一个数量级,单文件就不够了——这时候就需要一个真正的数据库了。
下一步:从零写一个迷你 SQL 数据库。这个记账本暴露的问题——单文件全量加载、没有索引——正是数据库要解决的问题。