Ziglings 笔记 106: 文件系统操作 (File System)
与磁盘对话
在 Ziglings 的第 106 个练习中,我们不再局限于控制台输出,而是开始在硬盘上留下痕迹。
Zig 的 std.fs 模块提供了一套非常现代的文件操作 API。它鼓励使用 相对路径 和 目录句柄,这比传统的绝对路径操作更安全、更高效。
挑战:创建并写入
我们需要编写一个程序,完成以下连贯动作:
- 在当前目录下创建一个名为
output的文件夹。 - 如果文件夹已存在,不要报错,继续执行。
- 打开这个文件夹。
- 在里面创建一个名为
zigling.txt的文件。 - 写入一句话 “It’s zigling time!”。
- 全程确保没有资源泄漏。
解决方案
这个练习展示了 Zig 错误处理和资源管理的完美结合:
const std = @import("std");
pub fn main() !void {
// 1. 获取当前工作目录 (Current Working Directory)
const cwd: std.fs.Dir = std.fs.cwd();
// 2. 创建目录 (带错误处理)
// makeDir 可能会失败。我们特别关注 PathAlreadyExists 错误。
// 如果目录已存在,我们 switch 捕获它并什么都不做 (=> {})。
// 如果是其他错误,我们将其返回 (=> return e)。
cwd.makeDir("output") catch |e| switch (e) {
error.PathAlreadyExists => {},
else => return e,
};
// 3. 打开目录
// 这里的 .{} 是 OpenDirOptions,使用默认值。
// 打开目录属于可能失败的操作,且我们无法恢复,所以用 try。
var output_dir: std.fs.Dir = try cwd.openDir("output", .{});
// 关键点:defer close
// 无论 main 函数如何结束,确保关闭目录句柄。
defer output_dir.close();
// 4. 创建文件
// createFile 默认会截断文件(如果已存在则清空)。
const file: std.fs.File = try output_dir.createFile("zigling.txt", .{});
// 关键点:defer close
// 文件句柄也是宝贵的资源,必须关闭。
defer file.close();
// 5. 写入数据
const bytes_written = try file.write("It's zigling time!");
std.debug.print("Successfully wrote {d} bytes.\n", .{bytes_written});
}
核心知识点总结
1. 为什么是 cwd?
Zig 所有的文件操作通常都基于一个 Dir 实例。std.fs.cwd() 是起点。这种设计避免了使用全局路径,让程序更容易在受限环境(如 WASI 或沙盒)中运行。
2. catch 的精细控制
catch |e| switch (e) { ... }
这是 Zig 错误处理的经典模式。它允许我们只忽略特定的错误(如“文件已存在”),而不会意外吞掉其他重要的错误(如“磁盘已满”或“权限被拒绝”)。
3. 配置选项
在 createFile("name", .{}) 中,那个不起眼的 .{} 实际上非常强大。它是 std.fs.File.CreateFlags。如果你想以追加模式打开文件,或者想在打开后同时读取,你可以这样写:
// 示例:允许读取
try dir.createFile("file.txt", .{ .read = true });
后续预告:我们已经成功写入了文件。那么,如何把文件内容读出来呢?如何读取一个不知道大小的文件?下一篇,我们将结合 Allocator 和 Reader,学习文件的读取操作。