Skip to content

Standard Input and Output

A program usually has three standard streams:

A program usually has three standard streams:

standard input
standard output
standard error

Standard input is where the program reads ordinary input. Standard output is where it writes ordinary output. Standard error is where it writes diagnostics.

In Zig:

const stdin = std.io.getStdIn();
const stdout = std.io.getStdOut();
const stderr = std.io.getStdErr();

These values are file-like handles.

A small program can read from standard input and write to standard output:

const std = @import("std");

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

    var buffer: [1024]u8 = undefined;

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

        if (n == 0)
            break;

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

This is a copy program. It reads bytes until end-of-file and writes the same bytes back.

Run it like this:

zig run main.zig

Type some text, then send end-of-file. On Unix systems this is usually Ctrl-D. On Windows it is usually Ctrl-Z followed by Enter.

The same program can read from a file by shell redirection:

zig run main.zig < input.txt

It can write to a file:

zig run main.zig > output.txt

It can do both:

zig run main.zig < input.txt > output.txt

No Zig code changed. The shell connected the streams.

Standard error is separate from standard output. This matters because diagnostics should not mix with program output.

const std = @import("std");

pub fn main() !void {
    const stderr = std.io.getStdErr();

    try stderr.writeAll("error: missing input\n");
}

With this separation, a program can write normal output to one place and errors to another:

zig run main.zig > output.txt 2> error.txt

For formatted output, use a writer:

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut();

    try stdout.writer().print(
        "answer = {d}\n",
        .{42},
    );
}

writeAll writes bytes. print formats values first.

For diagnostics, std.debug.print is common during development:

std.debug.print("x = {d}\n", .{x});

It writes to standard error.

This is useful because debug messages should not corrupt standard output, especially when output is piped into another program.

A filter program follows a simple rule:

read from standard input
write results to standard output
write diagnostics to standard error

That rule makes programs easy to connect.

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

Exercise 13-18. Change the program so it counts bytes and prints the count to standard error.

Exercise 13-19. Write a program that converts lowercase ASCII letters from standard input to uppercase.

Exercise 13-20. Write a program that prints usage information to standard error when given no arguments.