Reader Interface em Zig — O que é e Como Usar

Reader Interface em Zig — O que é e Como Usar

Definição

A Reader interface em Zig é o padrão utilizado pela biblioteca padrão para abstração de leitura de dados. Complementar à Writer interface, qualquer tipo que forneça uma função read com a assinatura correta pode ser usado como Reader. Isso permite escrever código genérico que lê de stdin, arquivos, buffers em memória, sockets de rede ou qualquer outra fonte de dados.

Por que Reader Interface Importa

  1. Código genérico: Funções que aceitam reader funcionam com qualquer fonte de dados.
  2. Testabilidade: Ler de buffer em testes, de arquivo em produção.
  3. Composição: Readers podem ser encadeados (buffered, limited, etc.).
  4. Processamento de streams: Ler dados incrementalmente sem carregar tudo na memória.

Exemplo Prático

Leitura Linha por Linha

const std = @import("std");

fn contarLinhas(reader: anytype) !usize {
    var linhas: usize = 0;
    while (true) {
        _ = reader.readUntilDelimiterOrEof('\n') catch |err| switch (err) {
            error.StreamTooLong => continue,
            else => return err,
        } orelse break;
        linhas += 1;
    }
    return linhas;
}

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 buffer: [4096]u8 = undefined;
    var linhas: usize = 0;

    while (true) {
        const linha = reader.readUntilDelimiterOrEof(&buffer, '\n') catch continue;
        if (linha == null) break;
        linhas += 1;
    }

    std.debug.print("Total de linhas: {}\n", .{linhas});
}

Ler Bytes com Reader Genérico

const std = @import("std");

fn lerExatamente(reader: anytype, n: usize, buffer: []u8) ![]u8 {
    if (n > buffer.len) return error.BufferPequeno;
    const lidos = try reader.readAll(buffer[0..n]);
    if (lidos < n) return error.FimPrematuro;
    return buffer[0..n];
}

test "ler de buffer em memória" {
    const dados = "Hello, Zig Brasil!";
    var stream = std.io.fixedBufferStream(dados);
    const reader = stream.reader();

    var buffer: [5]u8 = undefined;
    const resultado = try lerExatamente(reader, 5, &buffer);
    try std.testing.expectEqualStrings("Hello", resultado);
}

Buffered Reader para Performance

const std = @import("std");

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

    // Buffered reader reduz syscalls de leitura
    var buf_reader = std.io.bufferedReader(arquivo.reader());
    const reader = buf_reader.reader();

    var total: u64 = 0;
    var buffer: [4096]u8 = undefined;

    while (true) {
        const n = try reader.read(&buffer);
        if (n == 0) break; // EOF
        total += n;
    }

    std.debug.print("Total lido: {} bytes\n", .{total});
}

Readers Comuns na std lib

ReaderDescrição
std.io.getStdIn().reader()Entrada padrão (stdin)
file.reader()Leitura de arquivo
stream.reader()Leitura de fixedBufferStream
std.io.bufferedReader(r)Reader com buffer
std.io.limitedReader(r, n)Reader que limita bytes lidos

Métodos do Reader

MétodoDescrição
read(buffer)Ler até buffer.len bytes
readAll(buffer)Ler exatamente buffer.len bytes
readByte()Ler um único byte
readUntilDelimiterOrEof(buf, delim)Ler até delimitador ou EOF
skipBytes(n)Pular n bytes
readInt(T)Ler inteiro com endianness

Armadilhas Comuns

  • read retorna 0 no EOF: Sempre verifique se read() retornou 0 — isso indica fim dos dados.
  • read pode retornar menos bytes: Uma chamada a read() pode retornar menos bytes do que o buffer. Use readAll() quando precisar de quantidade exata.
  • Buffer insuficiente: readUntilDelimiterOrEof retorna erro se a linha exceder o buffer.
  • Performance sem buffer: Sem bufferedReader, cada readByte() gera uma syscall.

Termos Relacionados

Tutoriais Relacionados

Continue aprendendo Zig

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