Como Ler Arquivo Linha por Linha em Zig
Ler um arquivo linha por linha é essencial quando você precisa processar arquivos grandes sem carregar tudo na memória, ou quando quer processar cada linha individualmente (como em logs, CSV ou configurações).
Leitura Básica Linha por Linha
Use reader().readUntilDelimiterOrEof para ler uma linha por vez.
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
const arquivo = try std.fs.cwd().openFile("dados.txt", .{});
defer arquivo.close();
// Criar buffered reader para melhor desempenho
var buf_reader = std.io.bufferedReader(arquivo.reader());
const reader = buf_reader.reader();
var linha_buf: [4096]u8 = undefined;
var numero_linha: u32 = 0;
while (reader.readUntilDelimiterOrEof(&linha_buf, '\n')) |linha_opt| {
const linha = linha_opt orelse break;
numero_linha += 1;
// Remover \r se existir (Windows line endings)
const limpa = std.mem.trimRight(u8, linha, "\r");
try stdout.print("{d:>4}: {s}\n", .{ numero_linha, limpa });
} else |err| {
return err;
}
try stdout.print("\nTotal: {d} linhas\n", .{numero_linha});
}
Leitura com Alocação Dinâmica por Linha
Para linhas de tamanho arbitrário, use readUntilDelimiterAlloc.
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.txt", .{});
defer arquivo.close();
var buf_reader = std.io.bufferedReader(arquivo.reader());
const reader = buf_reader.reader();
var numero_linha: u32 = 0;
while (true) {
const linha = reader.readUntilDelimiterAlloc(allocator, '\n', 1024 * 1024) catch |err| {
if (err == error.EndOfStream) break;
return err;
};
defer allocator.free(linha);
numero_linha += 1;
const limpa = std.mem.trimRight(u8, linha, "\r");
try stdout.print("{d}: {s}\n", .{ numero_linha, limpa });
}
}
Coletar Todas as Linhas
Armazene todas as linhas em um ArrayList para processamento posterior.
const std = @import("std");
fn lerTodasLinhas(allocator: std.mem.Allocator, caminho: []const u8) !std.ArrayList([]u8) {
const arquivo = try std.fs.cwd().openFile(caminho, .{});
defer arquivo.close();
var buf_reader = std.io.bufferedReader(arquivo.reader());
const reader = buf_reader.reader();
var linhas = std.ArrayList([]u8).init(allocator);
errdefer {
for (linhas.items) |linha| allocator.free(linha);
linhas.deinit();
}
while (true) {
const linha = reader.readUntilDelimiterAlloc(allocator, '\n', 1024 * 1024) catch |err| {
if (err == error.EndOfStream) break;
return err;
};
const limpa = std.mem.trimRight(u8, linha, "\r");
if (limpa.len < linha.len) {
// Realocar sem o \r
const nova = try allocator.dupe(u8, limpa);
allocator.free(linha);
try linhas.append(nova);
} else {
try linhas.append(linha);
}
}
return linhas;
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var linhas = try lerTodasLinhas(allocator, "dados.txt");
defer {
for (linhas.items) |linha| allocator.free(linha);
linhas.deinit();
}
try stdout.print("Total de linhas: {d}\n", .{linhas.items.len});
for (linhas.items, 0..) |linha, i| {
try stdout.print(" [{d}] \"{s}\"\n", .{ i, linha });
}
}
Processar Linhas com Filtro
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
const arquivo = try std.fs.cwd().openFile("log.txt", .{});
defer arquivo.close();
var buf_reader = std.io.bufferedReader(arquivo.reader());
const reader = buf_reader.reader();
var linha_buf: [8192]u8 = undefined;
var erros: u32 = 0;
var avisos: u32 = 0;
while (reader.readUntilDelimiterOrEof(&linha_buf, '\n')) |linha_opt| {
const linha = linha_opt orelse break;
if (std.mem.indexOf(u8, linha, "[ERROR]") != null) {
erros += 1;
try stdout.print("ERRO: {s}\n", .{linha});
} else if (std.mem.indexOf(u8, linha, "[WARN]") != null) {
avisos += 1;
}
} else |err| {
return err;
}
try stdout.print("\nResumo: {d} erros, {d} avisos\n", .{ erros, avisos });
}
Exemplo Prático: Parser de Arquivo de Configuração
const std = @import("std");
const Config = struct {
entries: std.StringHashMap([]const u8),
allocator: std.mem.Allocator,
fn init(allocator: std.mem.Allocator) Config {
return .{
.entries = std.StringHashMap([]const u8).init(allocator),
.allocator = allocator,
};
}
fn deinit(self: *Config) void {
var it = self.entries.iterator();
while (it.next()) |entry| {
self.allocator.free(entry.key_ptr.*);
self.allocator.free(entry.value_ptr.*);
}
self.entries.deinit();
}
fn get(self: *const Config, chave: []const u8) ?[]const u8 {
return self.entries.get(chave);
}
};
fn carregarConfig(allocator: std.mem.Allocator, caminho: []const u8) !Config {
const arquivo = try std.fs.cwd().openFile(caminho, .{});
defer arquivo.close();
var buf_reader = std.io.bufferedReader(arquivo.reader());
const reader = buf_reader.reader();
var config = Config.init(allocator);
errdefer config.deinit();
var linha_buf: [4096]u8 = undefined;
while (reader.readUntilDelimiterOrEof(&linha_buf, '\n')) |linha_opt| {
const linha_raw = linha_opt orelse break;
const linha = std.mem.trim(u8, std.mem.trimRight(u8, linha_raw, "\r"), " \t");
// Ignorar linhas vazias e comentários
if (linha.len == 0 or linha[0] == '#' or linha[0] == ';') continue;
// Buscar separador '='
const sep = std.mem.indexOf(u8, linha, "=") orelse continue;
const chave = std.mem.trim(u8, linha[0..sep], " \t");
const valor = std.mem.trim(u8, linha[sep + 1 ..], " \t");
const chave_dup = try allocator.dupe(u8, chave);
const valor_dup = try allocator.dupe(u8, valor);
try config.entries.put(chave_dup, valor_dup);
} else |err| {
return err;
}
return config;
}
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var config = try carregarConfig(allocator, "app.conf");
defer config.deinit();
// Acessar valores
if (config.get("host")) |host| {
try stdout.print("Host: {s}\n", .{host});
}
if (config.get("porta")) |porta| {
try stdout.print("Porta: {s}\n", .{porta});
}
}
Veja Também
- Ler Conteúdo de Arquivo — Leia arquivo completo
- Dividir (Split) Strings — Processe cada linha
- Remover Espaços (Trim) — Limpe linhas lidas
- HashMap e StringHashMap — Armazene dados parseados