# Reading Bytes

### Reading Bytes

A file is read as bytes.

The simplest read call takes a buffer and returns the number of bytes placed in it.

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

The buffer may be larger than the data that was read. The value `n` tells which part is valid.

```zig
const bytes = buffer[0..n];
```

A complete program:

```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: [16]u8 = undefined;

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

        if (n == 0)
            break;

        const bytes = buffer[0..n];

        try std.io.getStdOut().writeAll(bytes);
    }
}
```

`read` does not promise to fill the whole buffer. It returns what was available.

For regular files, large reads often fill the buffer until the last read. For pipes, terminals, sockets, and other streams, short reads are normal. Correct code always uses `n`.

The end of the file is signaled by zero bytes read.

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

The slice `buffer[0..n]` is the data just read. It must be used before the next read if the next read reuses the same buffer.

This is the common form:

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

while (true) {
    const n = try file.read(&buffer);
    if (n == 0) break;

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

The size of the buffer is a choice. Small buffers are simple but may make more system calls. Large buffers reduce calls but use more stack space. For many programs, a buffer between 4 KiB and 64 KiB is a good starting point.

To read a whole small file, allocate storage and use a helper from the filesystem API. But for large files, streams, and unknown input, the loop above is the right shape.

Exercise 13-5. Change the buffer size to 1 byte and run the program.

Exercise 13-6. Count the total number of bytes read.

Exercise 13-7. Stop after reading the first 100 bytes.

Exercise 13-8. Replace the file with standard input.

