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
- Escrever em Arquivo — Grave dados em arquivos
- Ler Arquivo Linha por Linha — Processe arquivos grandes
- Verificar se Arquivo Existe — Verifique antes de ler
- Usando GeneralPurposeAllocator — Alocador para leitura