命令行里也能记账:用 C++ 写了个待办清单工具
【摘要】 学C++不能只刷算法题。本文用标准库 + 文件持久化,从零写一个能在终端跑的命令行待办工具,覆盖参数解析、数据结构、读写文件到 CMake 编译。
学 C++ 的人大多卡在"除了算法题不知道写啥"。其实终端里那些顺手的小工具,用 C++ 写起来特别合适——不需要界面、不需要框架,标准库就够。我前阵子写了个 todo 命令行工具,能加任务、列任务、标记完成,数据存本地文件,平时记点待办、临时的想法都靠它。

一、先定清楚要做什么
功能不用多,四个就够日常用:
todo add "写周报":新增一条任务todo list:列出所有任务,完成的打勾todo done 2:把第 2 条标记完成todo rm 3:删除第 3 条
数据存成纯文本 tasks.txt,每行一条,格式 id|title|done,用 | 分隔。选纯文本而不是 JSON,是因为它可读、能直接 cat 看,排查也方便。
二、核心结构长什么样
先定义一个 Task 结构,再准备读写文件的两个函数:
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
struct Task {
int id;
std::string title;
bool done;
};
std::vector<Task> loadTasks(const std::string& path) {
std::vector<Task> tasks;
std::ifstream in(path);
std::string line;
while (std::getline(in, line)) {
if (line.empty()) continue;
std::stringstream ss(line);
std::string idStr, title, doneStr;
std::getline(ss, idStr, '|');
std::getline(ss, title, '|');
std::getline(ss, doneStr, '|');
tasks.push_back({std::stoi(idStr), title, doneStr == "1"});
}
return tasks;
}
void saveTasks(const std::string& path, const std::vector<Task>& tasks) {
std::ofstream out(path);
for (const auto& t : tasks) {
out << t.id << '|' << t.title << '|' << (t.done ? '1' : '0') << '\n';
}
}
这里用 std::stringstream 按 | 切分,比手写 find + substr 清爽不少。loadTasks 读不到文件时会返回空 vector,所以第一次运行也不会崩。
三、解析参数把命令跑起来
main 里根据 argv[1] 分发命令,逻辑直来直去:
int main(int argc, char* argv[]) {
const std::string path = "tasks.txt";
if (argc < 2) {
std::cout << "用法: todo add|list|done|rm ...\n";
return 1;
}
std::string cmd = argv[1];
auto tasks = loadTasks(path);
if (cmd == "add") {
std::string title = argv[2];
int newId = tasks.empty() ? 1 : tasks.back().id + 1;
tasks.push_back({newId, title, false});
saveTasks(path, tasks);
std::cout << "已添加 #" << newId << "\n";
} else if (cmd == "list") {
for (const auto& t : tasks) {
std::cout << (t.done ? "[x] " : "[ ] ")
<< t.id << ". " << t.title << "\n";
}
} else if (cmd == "done") {
int id = std::stoi(argv[2]);
for (auto& t : tasks) if (t.id == id) t.done = true;
saveTasks(path, tasks);
} else if (cmd == "rm") {
int id = std::stoi(argv[2]);
tasks.erase(std::remove_if(tasks.begin(), tasks.end(),
[id](const Task& t){ return t.id == id; }), tasks.end());
saveTasks(path, tasks);
}
return 0;
}
done 和 rm 用线性查找定位 id,任务量小完全够用。等哪天任务上千条了,再换成 std::map 索引也不迟——别提前优化。
四、编译与几个注意点
用 g++ 直接编:
g++ -std=c++17 -O2 todo.cpp -o todo
./todo add "交水电费"
想正经点就用 CMake:
cmake_minimum_required(VERSION 3.10)
project(todo)
set(CMAKE_CXX_STANDARD 17)
add_executable(todo todo.cpp)
几个可能遇到的小坑:
- 中文参数:Windows 下
argv默认是本地编码(GBK),直接std::cout可能乱码;Linux 一般是 UTF-8 没问题。跨平台要处理得用std::filesystem和宽字符那套,简单场景就只在自己机器上跑、别纠结。 - 文件锁:这个工具是单用户的,没做并发锁。要是两个终端同时写,可能互相覆盖。真要多人用,得加
flock之类的文件锁。 - 参数校验:上面代码假定
argv[2]一定存在。实际用别的命令漏传参数会崩,正式版记得先判argc。 - id 重用:删掉中间一条后,id 不会回收。这是有意为之——回收 id 反而容易引起歧义,留空更直观。
结语
一个不到一百行的 C++ 程序,就能在终端里踏踏实实帮你记账、记待办。它没法跟那些花哨的 App 比功能,但胜在零依赖、秒开、数据完全在自己手里。学一门语言,最好的练手方式从来不是再做一遍算法题,而是写个自己每天真用得上的小东西。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)