std.io getStdOut, getStdErr, getStdIn em Zig — Referência e Exemplos

getStdOut, getStdErr, getStdIn — Fluxos Padrão

As funções getStdOut(), getStdErr() e getStdIn() fornecem acesso aos três fluxos padrão de I/O do processo: saída padrão, erro padrão e entrada padrão. Estes são os pontos de entrada mais comuns para interação com o terminal.

Visão Geral

Todo processo em sistemas Unix e Windows possui três descritores de arquivo padrão:

  • stdout (fd 1): Saída padrão para dados normais do programa
  • stderr (fd 2): Saída de erro para mensagens de diagnóstico e log
  • stdin (fd 0): Entrada padrão para receber dados do usuário ou de pipes
const std = @import("std");

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

Assinaturas

pub fn getStdOut() std.fs.File
pub fn getStdErr() std.fs.File
pub fn getStdIn() std.fs.File

Todas retornam um std.fs.File, que oferece métodos .writer() e .reader() para obter as respectivas interfaces de I/O.

Diferença entre stdout e stderr

A separação entre stdout e stderr é fundamental:

  • stdout: Dados de saída do programa (resultado, conteúdo gerado). Pode ser redirecionado com > no shell.
  • stderr: Mensagens de erro, avisos e informações de diagnóstico. Permanece no terminal mesmo com redirecionamento.
# No shell, stdout vai para o arquivo, stderr permanece no terminal
./meu_programa > saida.txt

Exemplo 1: Escrita em stdout e stderr

const std = @import("std");

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

    // Saída normal do programa — vai para stdout
    try stdout.print("Processando {d} itens...\n", .{100});

    // Simulação de processamento
    for (0..100) |i| {
        if (i % 25 == 0) {
            // Mensagem de progresso — vai para stderr
            try stderr.print("[INFO] Progresso: {d}%\n", .{i});
        }
    }

    // Resultado — vai para stdout
    try stdout.writeAll("Processamento concluído.\n");

    // Aviso — vai para stderr
    try stderr.writeAll("[AVISO] 3 itens foram ignorados.\n");
}

Exemplo 2: Leitura Interativa com stdin

const std = @import("std");

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

    try stdout.writeAll("=== Calculadora Simples ===\n\n");

    try stdout.writeAll("Digite o primeiro número: ");
    var buf: [64]u8 = undefined;

    const input1 = try stdin.readUntilDelimiterOrEof(&buf, '\n') orelse {
        try stdout.writeAll("Entrada vazia.\n");
        return;
    };

    const num1 = std.fmt.parseFloat(f64, input1) catch {
        try stdout.print("'{s}' não é um número válido.\n", .{input1});
        return;
    };

    try stdout.writeAll("Digite o segundo número: ");
    const input2 = try stdin.readUntilDelimiterOrEof(&buf, '\n') orelse {
        try stdout.writeAll("Entrada vazia.\n");
        return;
    };

    const num2 = std.fmt.parseFloat(f64, input2) catch {
        try stdout.print("'{s}' não é um número válido.\n", .{input2});
        return;
    };

    try stdout.print("\nResultados:\n", .{});
    try stdout.print("  {d} + {d} = {d}\n", .{ num1, num2, num1 + num2 });
    try stdout.print("  {d} - {d} = {d}\n", .{ num1, num2, num1 - num2 });
    try stdout.print("  {d} * {d} = {d}\n", .{ num1, num2, num1 * num2 });

    if (num2 != 0) {
        try stdout.print("  {d} / {d} = {d:.4}\n", .{ num1, num2, num1 / num2 });
    } else {
        try stderr.writeAll("[ERRO] Divisão por zero!\n");
    }
}

Exemplo 3: Programa de Filtro (stdin para stdout)

Programas de filtro leem de stdin e escrevem em stdout, como ferramentas Unix:

const std = @import("std");

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

    var buf_reader = std.io.bufferedReader(stdin);
    const reader = buf_reader.reader();
    var buf_writer = std.io.bufferedWriter(stdout);
    const writer = buf_writer.writer();

    var buf: [4096]u8 = undefined;
    var linhas_total: usize = 0;
    var linhas_filtradas: usize = 0;

    // Filtrar apenas linhas que contêm "TODO"
    while (reader.readUntilDelimiterOrEof(&buf, '\n')) |maybe_linha| {
        if (maybe_linha) |linha| {
            linhas_total += 1;
            if (std.mem.indexOf(u8, linha, "TODO")) |_| {
                linhas_filtradas += 1;
                try writer.print("{s}\n", .{linha});
            }
        } else break;
    } else |_| {}

    try buf_writer.flush();

    // Estatísticas para stderr (não poluem a saída filtrada)
    try stderr.print("Linhas processadas: {d}\n", .{linhas_total});
    try stderr.print("Linhas com TODO: {d}\n", .{linhas_filtradas});
}

Uso no shell:

cat codigo.zig | ./filtro_todo > todos.txt

Padrões Comuns

Debug Print vs stdout

O Zig oferece std.debug.print como atalho para escrita em stderr:

// Estas duas linhas são equivalentes:
std.debug.print("Debug: {d}\n", .{valor});
std.io.getStdErr().writer().print("Debug: {d}\n", .{valor}) catch {};

Note que std.debug.print ignora erros silenciosamente, enquanto o uso direto de stderr.writer().print() retorna o erro.

Verificação de Terminal

Você pode verificar se stdout é um terminal (TTY) ou está redirecionado:

const stdout_file = std.io.getStdOut();
if (stdout_file.supportsAnsiEscapeCodes()) {
    // Terminal com suporte a cores — usar ANSI
    try stdout_file.writer().writeAll("\x1b[32mSucesso!\x1b[0m\n");
} else {
    // Redirecionado para arquivo — sem cores
    try stdout_file.writer().writeAll("Sucesso!\n");
}

Padrão para Programas CLI

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

    executar(stdout, stderr) catch |err| {
        stderr.print("Erro fatal: {}\n", .{err}) catch {};
        std.process.exit(1);
    };
}

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.