zig-skills/references/std-fs.md

257 lines
7.3 KiB
Markdown

# File System API Reference (Zig 0.16.0)
Primary release-note source: https://ziglang.org/download/0.16.0/release-notes.html
Zig 0.16 migrates file-system operations to `std.Io`. Use `std.Io.Dir`, `std.Io.File`, and an explicit `std.Io` parameter. `std.fs` is now mostly path helpers and deprecated compatibility names.
The snippets below are focused fragments. They assume `const std =
@import("std")`, a caller-supplied `io: std.Io`, a suitable allocator, and any
named application values such as `max_size`, `from`, and `to`.
## Core Types
```zig
std.Io.Dir // Directory handle and file-system operations
std.Io.File // File handle, readers, writers, stat, locking
std.Io.File.Atomic
std.Io.File.MemoryMap
std.Io.Dir.path // Path helpers; std.fs.path is deprecated compatibility
```
## Current Directory
```zig
const cwd = std.Io.Dir.cwd();
```
Do not use old `std.fs.cwd()` in new 0.16 code.
## Opening Files
```zig
const file = try std.Io.Dir.cwd().openFile(io, "data.txt", .{});
defer file.close(io);
const rw = try std.Io.Dir.cwd().openFile(io, "data.txt", .{
.mode = .read_write,
});
defer rw.close(io);
```
## Creating Files
```zig
const file = try std.Io.Dir.cwd().createFile(io, "out.txt", .{});
defer file.close(io);
```
With default options, `createFile` truncates an existing regular file. Use the
exclusive-create option when overwriting must fail. Other options cover read
access, `permissions`, and locking where supported. File access mode is an
`openFile` option, not the name of the creation-permissions field.
## Reading Files
### Stream With Reader
```zig
const file = try std.Io.Dir.cwd().openFile(io, "data.txt", .{});
defer file.close(io);
var buffer: [4096]u8 = undefined;
var file_reader = file.reader(io, &buffer);
const reader = &file_reader.interface;
while (try reader.takeDelimiter('\n')) |line| {
_ = line;
}
```
### Allocate Whole File
```zig
const bytes = try std.Io.Dir.cwd().readFileAlloc(io, "data.txt", allocator, .limited(1024 * 1024));
defer allocator.free(bytes);
```
The limit uses `std.Io.Limit`. Hitting the limit returns `error.StreamTooLong`.
`readFileAlloc` creates a file reader internally and reads until the supplied
limit. Use an explicit `File.Reader` when streaming, reusing buffers, or
controlling incremental consumption.
### Read To End From Existing File
```zig
var buffer: [4096]u8 = undefined;
var file_reader = file.reader(io, &buffer);
const bytes = try file_reader.interface.allocRemaining(allocator, .limited(max_size));
defer allocator.free(bytes);
```
## Writing Files
### Stream With Writer
```zig
const file = try std.Io.Dir.cwd().createFile(io, "out.txt", .{});
defer file.close(io);
var buffer: [4096]u8 = undefined;
var file_writer = file.writer(io, &buffer);
const writer = &file_writer.interface;
try writer.print("value={d}\n", .{42});
try writer.writeAll("raw bytes\n");
try writer.flush();
```
### Write Whole File
```zig
try std.Io.Dir.cwd().writeFile(io, .{
.sub_path = "out.txt",
.data = "hello\n",
});
```
## Standard I/O
```zig
var stdout_buffer: [4096]u8 = undefined;
var stdout_writer = std.Io.File.stdout().writer(io, &stdout_buffer);
try stdout_writer.interface.print("hello\n", .{});
try stdout_writer.interface.flush();
var stderr_buffer: [4096]u8 = undefined;
var stderr_writer = std.Io.File.stderr().writer(io, &stderr_buffer);
try stderr_writer.interface.print("error: {s}\n", .{"message"});
try stderr_writer.interface.flush();
```
```zig
var stdin_buffer: [4096]u8 = undefined;
var stdin_reader = std.Io.File.stdin().reader(io, &stdin_buffer);
if (try stdin_reader.interface.takeDelimiter('\n')) |line| {
_ = line;
}
```
## Directories
```zig
var dir = try std.Io.Dir.cwd().openDir(io, "assets", .{ .iterate = true });
defer dir.close(io);
try std.Io.Dir.cwd().createDir(io, "new-dir", .default_dir);
try std.Io.Dir.cwd().createDirPath(io, "path/to/nested");
```
Both walking APIs require a directory opened with `.iterate = true`; iterating
without that capability is illegal behavior. Use other `openDir` options for
access/no-follow behavior as needed.
## Walking
Use `walk` for full recursive walks and `walkSelectively` when you want to decide which directories to enter.
```zig
var walker = try dir.walk(allocator);
defer walker.deinit();
while (try walker.next(io)) |entry| {
_ = entry;
}
```
```zig
var walker = try dir.walkSelectively(allocator);
defer walker.deinit();
while (try walker.next(io)) |entry| {
if (entry.kind == .directory and shouldEnter(entry)) {
try walker.enter(io, entry);
}
}
```
## Metadata
```zig
const stat = try file.stat(io);
const size = stat.size;
const kind = stat.kind;
const modified = stat.mtime;
const accessed = stat.atime orelse return error.FileAccessTimeUnavailable;
_ = .{ size, kind, modified, accessed };
```
`atime` is optional in 0.16.
Setting timestamps uses structured options:
```zig
try file.setTimestamps(io, .{
.access_timestamp = .init(stat.atime),
.modify_timestamp = .init(stat.mtime),
});
```
## Paths
`std.Io.Dir.path` / `std.fs.path` functions handle Windows paths more consistently in 0.16.
The relative-path helpers are pure and receive the current directory rather
than querying the OS. `relative` and `relativeWindows` also accept an optional
environment map for Windows per-drive current-directory resolution;
`relativePosix` has a distinct signature without that map.
```zig
const cwd_path = try std.process.currentPathAlloc(io, allocator);
defer allocator.free(cwd_path);
const rel = try std.fs.path.relative(allocator, cwd_path, environ_map, from, to);
defer allocator.free(rel);
```
## Atomic Files
Obtain a `std.Io.File.Atomic` value through `Dir.createFileAtomic`, then use its
public cleanup/materialization lifecycle. Do not construct `File.Atomic`
directly or hand-roll random temporary names; the helper routes entropy and
filesystem work through `std.Io`.
## Absolute Operations
Many old `std.fs.*Absolute` functions moved to `std.Io.Dir.*Absolute`, for example:
- `std.fs.openFileAbsolute` -> `std.Io.Dir.openFileAbsolute`
- `std.fs.createFileAbsolute` -> `std.Io.Dir.createFileAbsolute`
- `std.fs.deleteFileAbsolute` -> `std.Io.Dir.deleteFileAbsolute`
- `std.fs.renameAbsolute` -> `std.Io.Dir.renameAbsolute`
- `std.fs.accessAbsolute` -> `std.Io.Dir.accessAbsolute`
Many `Z` and `W` path-specific helpers were removed.
## Migration Map
| Old 0.15-style API | Zig 0.16 API |
|--------------------|--------------|
| `std.fs.cwd()` | `std.Io.Dir.cwd()` |
| `std.fs.File.stdout()` | `std.Io.File.stdout()` |
| `file.close()` | `file.close(io)` |
| `dir.close()` | `dir.close(io)` |
| `file.reader(&buf)` | `file.reader(io, &buf)` |
| `file.writer(&buf)` | `file.writer(io, &buf)` |
| `dir.readFileAlloc(allocator, path, max)` | `dir.readFileAlloc(io, path, allocator, .limited(max))` |
| `file.readToEndAlloc(allocator, max)` | `file.reader(io, &buf).interface.allocRemaining(allocator, .limited(max))` |
| `std.process.getCwdAlloc(allocator)` | `std.process.currentPathAlloc(io, allocator)` |
## Application Guidance
- Prefer an application's virtual or asset file-system abstraction for runtime reads when one exists.
- New direct file operations still need an `io` parameter at the API boundary.
- Writes should be explicit and use `std.Io.Dir` / `std.Io.File` until a project abstraction owns write policy.
- Avoid local fallback `Io.Threaded` construction in low-level file helpers; pass or store `io`.