Skip to content

Writing Bytes

Writing bytes is the opposite of reading them.

Writing bytes is the opposite of reading them.

The simplest write operation takes a slice and sends its contents to a file or stream.

try file.writeAll(bytes);

writeAll continues writing until every byte has been written or an error occurs.

A complete example:

const std = @import("std");

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

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

    const message = "hello, zig\n";

    try file.writeAll(message);
}

After running the program, output.txt contains:

hello, zig

The call:

cwd.createFile("output.txt", .{})

creates a new file for writing.

The returned value is a file handle. As before, the handle must eventually be closed.

The variable:

const message = "hello, zig\n";

is a string literal.

A Zig string literal is stored as bytes. Its type is a pointer to a constant array of u8 values with a sentinel byte at the end.

writeAll accepts any byte slice, including string literals.

The operation:

try file.writeAll(message);

does not add formatting, terminators, or extra bytes. The exact bytes from the slice are written.

A larger example copies standard input into a file.

const std = @import("std");

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

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

    var buffer: [1024]u8 = undefined;

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

        if (n == 0)
            break;

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

This program has the same structure as the previous reading examples:

  1. create a resource
  2. allocate a buffer
  3. read bytes
  4. write bytes
  5. repeat until end-of-file

The important point is that writing may also be partial.

Some operating system calls write fewer bytes than requested. The method:

writeAll

handles this automatically.

The lower-level operation:

write

returns the number of bytes actually written:

const n = try file.write(bytes);

Like read, correct code must use the returned value.

Most programs should prefer writeAll unless partial writes are required explicitly.

Standard output is also a writable stream.

const stdout = std.io.getStdOut();

try stdout.writeAll("hello\n");

Or through a writer:

try std.io.getStdOut().writer().print(
    "value = {}\n",
    .{42},
);

The difference is important:

  • writeAll writes raw bytes
  • print formats values into bytes first

Programs often combine both styles.

Exercise 13-9. Write a program that writes the numbers 1 through 10 into a file.

Exercise 13-10. Write a program that copies one file into another using only read and write, not readAll or writeAll.

Exercise 13-11. Modify the copy program so it appends instead of replacing the output file.

Exercise 13-12. Write a program that writes all command-line arguments into a file, one per line.