std.io.Reader em Zig — Referência e Exemplos

std.io.Reader — Interface de Leitura

A interface Reader é o mecanismo genérico do Zig para leitura de bytes. Assim como o Writer, ela é parametrizada em tempo de compilação, permitindo que o mesmo código funcione com arquivos, sockets, buffers em memória e qualquer outro tipo que forneça uma função de leitura.

Visão Geral

O Reader é definido pelo tipo parametrizado GenericReader:

pub fn GenericReader(
    comptime Context: type,
    comptime ReadError: type,
    comptime readFn: fn (Context, []u8) ReadError!usize,
) type

A função readFn deve:

  • Preencher o buffer fornecido com dados lidos
  • Retornar o número de bytes efetivamente lidos
  • Retornar 0 para indicar fim do fluxo (EOF)

Métodos Principais

Leitura Básica

// Lê bytes para o buffer; retorna quantos foram lidos (0 = EOF)
pub fn read(self: Self, buffer: []u8) ReadError!usize

// Lê exatamente buffer.len bytes ou retorna erro
pub fn readAll(self: Self, buffer: []u8) ReadError!usize

// Lê exatamente N bytes; retorna erro se não conseguir
pub fn readNoEof(self: Self, buf: *[N]u8) (ReadError || error{EndOfStream})!void

// Lê um único byte
pub fn readByte(self: Self) (ReadError || error{EndOfStream})!u8

Leitura por Delimitador

// Lê até encontrar o delimitador ou EOF
pub fn readUntilDelimiterOrEof(
    self: Self,
    buf: []u8,
    delimiter: u8,
) (ReadError || error{StreamTooLong})!?[]u8

// Lê até o delimitador, alocando memória conforme necessário
pub fn readUntilDelimiterAlloc(
    self: Self,
    allocator: std.mem.Allocator,
    delimiter: u8,
    max_size: usize,
) (ReadError || error{StreamTooLong} || Allocator.Error)![]u8

Leitura de Tipos

// Lê um inteiro com endianness especificado
pub fn readInt(self: Self, comptime T: type, endian: std.builtin.Endian) !T

// Lê uma struct empacotada (packed)
pub fn readStruct(self: Self, comptime T: type) !T

Controle de Fluxo

// Descarta N bytes do fluxo
pub fn skipBytes(self: Self, num_bytes: u64, options: SkipBytesOptions) !void

// Lê e descarta todos os bytes restantes
pub fn discard(self: Self) !u64

// Verifica se o reader ainda tem dados
pub fn any(self: Self) bool

Exemplo 1: Leitura Linha por Linha

O caso de uso mais comum é ler um fluxo linha por linha:

const std = @import("std");

pub fn main() !void {
    const arquivo = try std.fs.cwd().openFile("dados.txt", .{});
    defer arquivo.close();

    var buf_reader = std.io.bufferedReader(arquivo.reader());
    const reader = buf_reader.reader();

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

    while (reader.readUntilDelimiterOrEof(&buf, '\n')) |maybe_linha| {
        if (maybe_linha) |linha| {
            num_linha += 1;
            std.debug.print("{d:>4}: {s}\n", .{ num_linha, linha });
        } else {
            break; // EOF
        }
    } else |err| {
        std.debug.print("Erro na leitura: {}\n", .{err});
    }

    std.debug.print("\nTotal: {d} linhas\n", .{num_linha});
}

Exemplo 2: Leitura da Entrada Padrão com Alocação

Usando readUntilDelimiterAlloc para linhas de tamanho variável:

const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const stdin = std.io.getStdIn().reader();
    const stdout = std.io.getStdOut().writer();

    try stdout.writeAll("Digite palavras (uma por linha, 'sair' para encerrar):\n");

    var palavras = std.ArrayList([]const u8).init(allocator);
    defer {
        for (palavras.items) |p| allocator.free(p);
        palavras.deinit();
    }

    while (true) {
        try stdout.writeAll("> ");
        const linha = stdin.readUntilDelimiterAlloc(
            allocator,
            '\n',
            1024 * 1024, // máximo 1 MB por linha
        ) catch |err| {
            std.debug.print("Erro: {}\n", .{err});
            break;
        };

        if (std.mem.eql(u8, linha, "sair")) {
            allocator.free(linha);
            break;
        }

        try palavras.append(linha);
    }

    try stdout.print("\nVocê digitou {d} palavras:\n", .{palavras.items.len});
    for (palavras.items, 0..) |palavra, i| {
        try stdout.print("  {d}. {s}\n", .{ i + 1, palavra });
    }
}

Exemplo 3: Leitura de Dados Binários

Lendo um formato binário simples (cabeçalho + dados):

const std = @import("std");

const Cabecalho = packed struct {
    magic: u32,
    versao: u16,
    num_registros: u16,
};

fn lerDadosBinarios(reader: anytype) !void {
    // Ler cabeçalho
    const cabecalho = try reader.readStruct(Cabecalho);

    if (cabecalho.magic != 0x5A494742) { // "ZIGB"
        return error.FormatoInvalido;
    }

    std.debug.print("Versão: {d}, Registros: {d}\n", .{
        cabecalho.versao,
        cabecalho.num_registros,
    });

    // Ler cada registro (inteiros de 32 bits)
    for (0..cabecalho.num_registros) |i| {
        const valor = try reader.readInt(u32, .little);
        std.debug.print("  Registro {d}: {d}\n", .{ i, valor });
    }
}

pub fn main() !void {
    const arquivo = try std.fs.cwd().openFile("dados.bin", .{});
    defer arquivo.close();
    try lerDadosBinarios(arquivo.reader());
}

Padrões Comuns

Leitura Completa para Memória

Para ler um arquivo inteiro de uma vez:

const conteudo = try std.fs.cwd().readFileAlloc(allocator, "config.txt", 1024 * 1024);
defer allocator.free(conteudo);

Iteração com readUntilDelimiterOrEof

O padrão idiomático para processar todas as linhas:

var buf: [4096]u8 = undefined;
while (try reader.readUntilDelimiterOrEof(&buf, '\n')) |linha| {
    // processar `linha`
}
// Chegou ao EOF

Combinando Reader com BufferedReader

Sempre que ler de um arquivo ou socket, use buffering para melhor desempenho:

var buffered = std.io.bufferedReader(fonte.reader());
const reader = buffered.reader();

Erros Comuns

  • error.EndOfStream: Tentou ler mais bytes do que os disponíveis com readNoEof
  • error.StreamTooLong: A linha/token excedeu o tamanho do buffer fornecido

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

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