---
title: "Reader Interface em Zig — O que é e Como Usar"
url: "https://ziglang.com.br/glossario/reader-interface-em-zig-o-que-%C3%A9-e-como-usar/"
markdown_url: "https://ziglang.com.br/glossario/reader-interface-em-zig-o-que-%C3%A9-e-como-usar.MD"
description: "Entenda a Reader interface em Zig: padrão para leitura genérica de arquivos, buffers, sockets e qualquer fonte de dados. Guia pt-BR."
date: "2026-02-21"
author: "Zig Brasil"
---

# Reader Interface em Zig — O que é e Como Usar

Entenda a Reader interface em Zig: padrão para leitura genérica de arquivos, buffers, sockets e qualquer fonte de dados. Guia pt-BR.


# 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

```zig
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

```zig
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

```zig
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

| Reader | Descriçã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étodo | Descriçã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.

## Implementando seu Próprio Reader

Qualquer struct que implemente o método `read` com a assinatura correta pode ser usada como Reader. Isso é feito via `std.io.GenericReader`:

```zig
const std = @import("std");

// Reader que repete um byte infinitamente
const RepeatReader = struct {
    byte: u8,

    pub const Reader = std.io.Reader(*RepeatReader, error{}, lerBytes);

    fn lerBytes(self: *RepeatReader, buffer: []u8) error{}!usize {
        for (buffer) |*b| b.* = self.byte;
        return buffer.len;
    }

    pub fn reader(self: *RepeatReader) Reader {
        return .{ .context = self };
    }
};

test "repeat reader" {
    var rep = RepeatReader{ .byte = 'A' };
    var buf: [5]u8 = undefined;
    _ = try rep.reader().readAll(&buf);
    try std.testing.expectEqualStrings("AAAAA", &buf);
}
```

## Boas Práticas com Reader

**Sempre use `bufferedReader` ao ler de arquivo ou socket**: Sem buffer, cada chamada a `readByte()` ou `readUntilDelimiterOrEof` gera uma syscall separada. O buffered reader agrupa as syscalls em blocos de 4096 bytes por padrão.

**Prefira `readAll` a `read` quando precisar de quantidade exata**: `read()` pode retornar menos bytes que o solicitado — isso não é erro, é comportamento normal de I/O. `readAll()` garante que o buffer seja preenchido completamente ou retorna `error.EndOfStream`.

**Use `anytype` para funções genéricas de leitura**: Isso permite que a função aceite qualquer tipo de reader sem overhead de vtable:

```zig
fn parsearCSV(reader: anytype, allocator: std.mem.Allocator) ![][]u8 {
    var linhas = std.ArrayList([]u8).init(allocator);
    var buf: [4096]u8 = undefined;
    while (try reader.readUntilDelimiterOrEof(&buf, '\n')) |linha| {
        try linhas.append(try allocator.dupe(u8, linha));
    }
    return linhas.toOwnedSlice();
}
```

## Termos Relacionados

- [Writer Interface](/glossario/writer-interface/) — Interface de escrita complementar
- [anytype](/glossario/anytype/) — Mecanismo de genéricos usado pela interface
- [Slice](/glossario/slice/) — Buffers são slices de bytes
- [Error Union](/glossario/error-union/) — Readers retornam erros de I/O

## Tutoriais Relacionados

- [I/O em Zig](/tutoriais/io-em-zig/)
- [Zig Design Patterns](/tutoriais/zig-design-patterns/)
- [Strings e Arrays em Zig](/tutoriais/strings-e-arrays-zig/)
