Como Ler Conteúdo de Arquivo em Zig

Como Ler Conteúdo de Arquivo em Zig

Ler arquivos é uma das operações mais fundamentais em programação. Zig oferece uma API limpa e eficiente para leitura de arquivos através do módulo std.fs, com controle explícito sobre alocação de memória.

Ler Arquivo Completo com Alocador

A forma mais simples de ler um arquivo inteiro é usando readToEndAlloc ou a função de conveniência do Dir.

const std = @import("std");

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

    // Abrir e ler arquivo completo
    const arquivo = try std.fs.cwd().openFile("exemplo.txt", .{});
    defer arquivo.close();

    const conteudo = try arquivo.readToEndAlloc(allocator, 1024 * 1024); // máximo 1MB
    defer allocator.free(conteudo);

    try stdout.print("Conteúdo ({d} bytes):\n{s}\n", .{ conteudo.len, conteudo });
}

Ler Arquivo com Caminho Absoluto

const std = @import("std");

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

    // Abrir por caminho absoluto
    const arquivo = try std.fs.openFileAbsolute("/etc/hostname", .{});
    defer arquivo.close();

    const conteudo = try arquivo.readToEndAlloc(allocator, 4096);
    defer allocator.free(conteudo);

    const limpo = std.mem.trim(u8, conteudo, " \t\n\r");
    try stdout.print("Hostname: {s}\n", .{limpo});
}

Ler em Buffer Fixo (Sem Alocação Dinâmica)

Para evitar alocação dinâmica, leia diretamente em um buffer na stack.

const std = @import("std");

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

    const arquivo = try std.fs.cwd().openFile("exemplo.txt", .{});
    defer arquivo.close();

    // Ler em buffer fixo
    var buf: [4096]u8 = undefined;
    const bytes_lidos = try arquivo.readAll(&buf);

    try stdout.print("Lidos {d} bytes:\n{s}\n", .{ bytes_lidos, buf[0..bytes_lidos] });
}

Obter Tamanho do Arquivo

Antes de alocar memória, pode ser útil saber o tamanho do arquivo.

const std = @import("std");

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

    const arquivo = try std.fs.cwd().openFile("exemplo.txt", .{});
    defer arquivo.close();

    // Obter metadados (tamanho, timestamps, etc.)
    const stat = try arquivo.stat();
    try stdout.print("Tamanho: {d} bytes\n", .{stat.size});
    try stdout.print("Tipo: {}\n", .{stat.kind});
}

Ler Arquivo Binário

A leitura de arquivos binários segue o mesmo padrão, mas você trabalha com bytes brutos.

const std = @import("std");

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

    const arquivo = try std.fs.cwd().openFile("dados.bin", .{});
    defer arquivo.close();

    const dados = try arquivo.readToEndAlloc(allocator, 1024 * 1024);
    defer allocator.free(dados);

    // Exibir primeiros bytes em hexadecimal
    try stdout.print("Primeiros bytes: ", .{});
    for (dados[0..@min(16, dados.len)]) |byte| {
        try stdout.print("{X:0>2} ", .{byte});
    }
    try stdout.print("\n", .{});
    try stdout.print("Total: {d} bytes\n", .{dados.len});
}

Ler com Leitura Parcial (read)

A função read pode retornar menos bytes do que o solicitado. Use-a quando quiser processar dados em partes.

const std = @import("std");

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

    const arquivo = try std.fs.cwd().openFile("exemplo.txt", .{});
    defer arquivo.close();

    var buf: [64]u8 = undefined;
    var total: usize = 0;

    while (true) {
        const n = try arquivo.read(&buf);
        if (n == 0) break; // Fim do arquivo
        total += n;
        try stdout.print("{s}", .{buf[0..n]});
    }

    try stdout.print("\n--- Total: {d} bytes ---\n", .{total});
}

Tratamento de Erros

Trate erros comuns ao abrir e ler arquivos.

const std = @import("std");

fn lerArquivoSeguro(caminho: []const u8, allocator: std.mem.Allocator) ![]u8 {
    const arquivo = std.fs.cwd().openFile(caminho, .{}) catch |err| {
        const stderr = std.io.getStdErr().writer();
        switch (err) {
            error.FileNotFound => try stderr.print("Erro: Arquivo '{s}' não encontrado\n", .{caminho}),
            error.AccessDenied => try stderr.print("Erro: Sem permissão para ler '{s}'\n", .{caminho}),
            else => try stderr.print("Erro ao abrir '{s}': {}\n", .{ caminho, err }),
        }
        return err;
    };
    defer arquivo.close();

    return arquivo.readToEndAlloc(allocator, 10 * 1024 * 1024) catch |err| {
        const stderr = std.io.getStdErr().writer();
        switch (err) {
            error.OutOfMemory => try stderr.print("Erro: Arquivo muito grande para ler\n", .{}),
            else => try stderr.print("Erro ao ler: {}\n", .{err}),
        }
        return err;
    };
}

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

    // Tentar ler arquivo que pode não existir
    if (lerArquivoSeguro("config.txt", allocator)) |conteudo| {
        defer allocator.free(conteudo);
        try stdout.print("Configuração: {s}\n", .{conteudo});
    } else |_| {
        try stdout.print("Usando configuração padrão\n", .{});
    }
}

Exemplo Prático: Contar Linhas e Palavras

const std = @import("std");

const Estatisticas = struct {
    linhas: usize,
    palavras: usize,
    bytes: usize,
};

fn contarEstatisticas(conteudo: []const u8) Estatisticas {
    var stats = Estatisticas{ .linhas = 0, .palavras = 0, .bytes = conteudo.len };

    // Contar linhas
    for (conteudo) |c| {
        if (c == '\n') stats.linhas += 1;
    }
    if (conteudo.len > 0 and conteudo[conteudo.len - 1] != '\n') {
        stats.linhas += 1;
    }

    // Contar palavras
    var tokens = std.mem.tokenizeAny(u8, conteudo, " \t\n\r");
    while (tokens.next()) |_| {
        stats.palavras += 1;
    }

    return stats;
}

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

    const arquivo = try std.fs.cwd().openFile("exemplo.txt", .{});
    defer arquivo.close();

    const conteudo = try arquivo.readToEndAlloc(allocator, 10 * 1024 * 1024);
    defer allocator.free(conteudo);

    const stats = contarEstatisticas(conteudo);
    try stdout.print("Linhas:   {d}\n", .{stats.linhas});
    try stdout.print("Palavras: {d}\n", .{stats.palavras});
    try stdout.print("Bytes:    {d}\n", .{stats.bytes});
}

Veja Também

Continue aprendendo Zig

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