用Go实现TODO命令行应用需稳控os.Args解析、JSON持久化和文件读写:用flag包处理参数避免手动切分错误,用os.ReadFile/os.WriteFile安全存取todos.json,Todo结构体带自增ID并规范JSON标签,路径用os.UserConfigDir跨平台适配。

用 Go 写一个命令行 TODO 应用,核心不在于功能多全,而在于把 os.Args、文件读写、JSON 序列化这三块稳稳接住——多数人卡在数据持久化时的竞态或编码错位上。
解析命令行参数:别硬写 switch,用 flag 包更可靠
直接操作 os.Args 容易漏掉空格、引号、子命令嵌套等问题。比如输入 todo add "buy milk",手动切分可能把引号当内容一部分。
- 用
flag.String("add", "", "添加待办事项")配合flag.Parse(),自动处理引号和空格 - 子命令(如
todo list/todo done 3)建议用flag.Arg(0)判断主动作,再用后续flag.Args()拿参数,比自定义 parser 轻量 - 避免在
init()里调用flag.Parse()——它会提前消费os.Args,导致后续逻辑拿不到原始参数
持久化到 JSON 文件:注意 os.OpenFile 的 flag 组合
TODO 数据通常存成 todos.json,但常见错误是用 os.Create() 每次覆盖,或用 os.OpenFile(..., os.O_APPEND) 却忘了先读旧数据。
- 正确流程:先
os.ReadFile("todos.json")尝试读;若os.IsNotExist(err)再初始化空切片 - 写入时用
os.WriteFile("todos.json", data, 0644),比os.OpenFile+json.Encoder更简洁,且自动处理文件创建/截断 - 如果并发运行(比如多个终端同时操作),需加文件锁——但单用户 CLI 场景下,优先保证“每次读-改-写”原子性,用临时文件 +
os.Rename()替代直接覆盖
任务 ID 管理:别用数组下标当 ID
很多人让 todo list 输出时显示 [0] xxx,然后 todo done 0 去标记完成——这在删除中间项后必然错乱。
立即学习“go语言免费学习笔记(深入)”;
- 每个
Todo结构体必须带自增ID int字段,插入时取当前最大 ID + 1(可用len(todos) + 1简单实现,前提是不支持删除) - 若支持删除,ID 必须持久化进 JSON,且查找时用
for _, t := range todos { if t.ID == targetID { ... } },而非靠索引 - 显示列表时,用
fmt.Printf("%d. [%s] %s\n", t.ID, status, t.Text),用户操作始终基于这个ID
跨平台路径与默认文件位置:别写死 "./todos.json"
Windows 下 C:\Users\Me\go\todo\todos.json 和 macOS 的 ~/Library/Application Support/todo/todos.json 体验差很多。
- 用
os.UserHomeDir()+filepath.Join()构造路径,例如:filepath.Join(home, ".todo.json") - 更规范的做法是调用
os.UserConfigDir()(Go 1.13+),它会返回系统推荐的配置目录(Windows 是%APPDATA%,macOS 是~/Library/Application Support) - 开发调试时可加
-datafile标志覆盖路径,方便测试不同位置
最常被跳过的细节是 JSON 字段标签和时间字段处理:CreatedAt time.Time `json:"created_at"` 必须加 time.Time 的 JSON 支持(默认只序列化为 RFC3339 字符串),否则读出来会是空值;另外,不要用 map[string]interface{} 存 TODO,结构体 + 显式字段才是可维护的基础。










