应使用 json.marshal 序列化结构体后通过 os.writefile 原子写入文件,确保字段导出、加 json 标签,读取时用 os.readfile + json.unmarshal 并校验错误与数据完整性。

用 json.Marshal 和 os.WriteFile 保存学生成绩到文件
Go 没有内置数据库,本地存储最直接的方式就是序列化为 JSON 写入文件。别用 fmt.Fprintln 拼字符串,也别手动构造 JSON 字段——容易出错且无法反序列化。
关键点:
-
struct字段必须首字母大写(导出),否则json.Marshal会忽略 - 建议加
json:标签统一字段名,比如StudentID int `json:"student_id"` - 写文件前先调用
json.Marshal验证数据可序列化,避免静默失败 - 用
os.WriteFile(filename, data, 0644)替代os.OpenFile+Write组合,更简洁安全
示例片段:
type Student struct {
StudentID int `json:"student_id"`
Name string `json:"name"`
Score float64 `json:"score"`
}
data, err := json.Marshal(students)
if err != nil {
log.Fatal(err)
}
os.WriteFile("scores.json", data, 0644)
用 json.Unmarshal 从文件读取成绩并校验结构
读取时常见错误是直接 json.Unmarshal 到未初始化的切片或 map,导致 panic 或空数据。必须提前声明变量并传指针。
立即学习“go语言免费学习笔记(深入)”;
还要注意:
- 文件不存在时
os.ReadFile返回os.ErrNotExist,不是nil错误,需单独判断,否则后续Unmarshal会 panic - JSON 字段缺失时,对应 struct 字段会被设为零值(
0、""、false),不报错但可能掩盖数据问题 - 若允许部分字段为空,可在 struct tag 加
omitempty;若要求必填,得额外写校验逻辑(比如检查Name != "")
推荐写法:
data, err := os.ReadFile("scores.json")
if errors.Is(err, os.ErrNotExist) {
return []Student{}, nil // 文件不存在,返回空切片
}
if err != nil {
return nil, err
}
var students []Student
if err := json.Unmarshal(data, &students); err != nil {
return nil, fmt.Errorf("invalid JSON in scores.json: %w", err)
}
避免并发写入时文件被覆盖或损坏
多个 goroutine 同时调用 os.WriteFile 写同一个文件,结果不可预测:可能丢数据、写入截断、甚至 JSON 格式破坏(比如两个 Marshal 结果拼在一起)。
简单系统不用上完整锁服务,但至少要:
- 所有对同一文件的读写操作,通过一个
sync.Mutex串行化 - 不要在 goroutine 里直接写文件——把数据传给一个专门的“持久化 goroutine”,由它统一处理写入(配合
chan []Student) - 写入前先
json.Marshal到内存,再原子写入(os.WriteFile本身是原子的),避免中间状态残留
如果后期要支持多进程访问,就得换用 SQLite(用 mattn/go-sqlite3)或加文件锁(gofrs/flock),纯 JSON 文件不适合高并发场景。
增删改查逻辑里最容易漏掉的边界情况
学生 ID 重复、查不到学生、删除空列表、修改时 ID 不存在——这些不是“异常”,而是正常业务流,代码里不能 panic 或直接 return 错误。
实操建议:
- 查找函数返回
(*Student, bool),用布尔值表示“是否找到”,比返回nil+ error 更清晰 - 删除操作先
findIndex,再append(s[:i], s[i+1:]...),别用copy或循环移位,易越界 - 新增学生前检查
StudentID是否已存在(遍历或用map[int]struct{}缓存 ID 集合) - 所有输入数据(尤其是从命令行或 HTTP 请求来的)必须做基础校验:
Score在 0–100 之间、Name非空、StudentID > 0
文件存储本身不提供约束,所有规则都得在 Go 层手动兜住——这恰恰是练手时最该写清楚的部分。










