# Files

### Files

Most programs spend their time moving bytes.

They read files, transform data, and write results somewhere else. Zig exposes these operations directly. There is no hidden runtime. A file is an operating system resource, and Zig's standard library provides functions that work close to the system interface.

This chapter begins with the simplest possible file operation: opening a file and reading its contents.

```zig
const std = @import("std");

pub fn main() !void {
    const cwd = std.fs.cwd();

    const file = try cwd.openFile("message.txt", .{});
    defer file.close();

    var buffer: [128]u8 = undefined;

    const n = try file.readAll(&buffer);

    try std.io.getStdOut().writer().print(
        "{s}\n",
        .{buffer[0..n]},
    );
}
```

Suppose `message.txt` contains:

```text
hello from zig
```

Running the program prints:

```text
hello from zig
```

The program begins by getting the current working directory:

```zig
const cwd = std.fs.cwd();
```

`cwd` is a filesystem handle. Most filesystem operations begin from a directory handle rather than from global functions.

The next statement opens the file:

```zig
const file = try cwd.openFile("message.txt", .{});
```

The first argument is the path. The second argument is a structure containing open options. Here the structure is empty:

```zig
.{}
```

The defaults are enough for a simple read-only open.

The return value is a file handle. A file handle owns an operating system resource, so it must eventually be closed.

```zig
defer file.close();
```

`defer` schedules the call to happen when the surrounding scope ends. No matter how the function exits, `file.close()` runs before the function returns.

The buffer declaration creates space for file data:

```zig
var buffer: [128]u8 = undefined;
```

This is an array of 128 bytes.

`undefined` means the memory begins with unspecified contents. Zig does not automatically clear stack memory.

The next statement reads bytes into the buffer:

```zig
const n = try file.readAll(&buffer);
```

`readAll` fills the buffer and returns the number of bytes actually read.

The expression:

```zig
&buffer
```

passes a pointer to the array.

The returned value `n` is important. A file may contain fewer bytes than the buffer size, so only the range:

```zig
buffer[0..n]
```

contains valid data.

This expression creates a slice. A slice is a pointer plus a length.

The final statement prints the slice as a string:

```zig
try std.io.getStdOut().writer().print(
    "{s}\n",
    .{buffer[0..n]},
);
```

`{s}` formats a byte slice as a string.

The function returns:

```zig
!void
```

The `!` means the function may return an error. Every filesystem operation can fail:

- the file may not exist
- permissions may deny access
- the disk may fail
- the path may refer to a directory

The `try` operator handles these failures by returning the error immediately to the caller.

A slightly larger example copies one file into another.

```zig
const std = @import("std");

pub fn main() !void {
    const cwd = std.fs.cwd();

    const input = try cwd.openFile("input.txt", .{});
    defer input.close();

    const output = try cwd.createFile(
        "output.txt",
        .{},
    );
    defer output.close();

    var buffer: [1024]u8 = undefined;

    while (true) {
        const n = try input.read(&buffer);

        if (n == 0)
            break;

        try output.writeAll(buffer[0..n]);
    }
}
```

The loop continues until `read` returns zero bytes:

```zig
if (n == 0)
    break;
```

A return value of zero means end-of-file.

This style appears often in systems programs:

1. open resources
2. allocate a buffer
3. read bytes
4. process bytes
5. write bytes
6. close resources

Zig keeps each step visible.

Exercise 13-1. Modify the first program so it prints the file size.

Exercise 13-2. Write a program that copies standard input to standard output.

Exercise 13-3. Change the copy program so it counts lines while copying.

Exercise 13-4. Write a program that prints the first 10 bytes of a file in hexadecimal.

