Cheatsheet: Operações de I/O em Zig
Zig oferece um sistema de I/O robusto e performático através da biblioteca padrão std.fs e std.io. O design prioriza controle explícito sobre buffers, tratamento de erros obrigatório e composição de readers/writers via interfaces genéricas.
Stdout, Stdin e Stderr
Escrita em stdout
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
try stdout.print("Olá, {s}!\n", .{"mundo"});
try stdout.writeAll("Texto direto sem formatação\n");
// Escrever bytes brutos
try stdout.writeAll(&[_]u8{ 72, 105, 10 }); // "Hi\n"
// Escrever byte único
try stdout.writeByte('Z');
try stdout.writeByte('\n');
}
Leitura de stdin
const std = @import("std");
pub fn main() !void {
const stdin = std.io.getStdIn().reader();
const stdout = std.io.getStdOut().writer();
try stdout.writeAll("Digite seu nome: ");
// Ler uma linha (até \n, máximo 256 bytes)
var buffer: [256]u8 = undefined;
const linha = try stdin.readUntilDelimiterOrEof(&buffer, '\n');
if (linha) |nome| {
try stdout.print("Olá, {s}!\n", .{nome});
}
}
Stderr para erros e debug
const std = @import("std");
pub fn main() !void {
const stderr = std.io.getStdErr().writer();
try stderr.print("ERRO: {s}\n", .{"arquivo não encontrado"});
// Ou usar std.debug.print (sempre vai para stderr)
std.debug.print("Debug: valor = {d}\n", .{42});
}
Ler arquivo inteiro
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Ler arquivo inteiro para string
const conteudo = try std.fs.cwd().readFileAlloc(
allocator,
"dados.txt",
1024 * 1024, // máximo 1MB
);
defer allocator.free(conteudo);
std.debug.print("Conteúdo: {s}\n", .{conteudo});
}
Ler arquivo linha por linha
const std = @import("std");
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 linha_buf: [4096]u8 = undefined;
var numero_linha: usize = 1;
while (try reader.readUntilDelimiterOrEof(&linha_buf, '\n')) |linha| {
std.debug.print("{d}: {s}\n", .{ numero_linha, linha });
numero_linha += 1;
}
}
Escrever em arquivo
const std = @import("std");
pub fn main() !void {
// Criar/sobrescrever arquivo
const arquivo = try std.fs.cwd().createFile("saida.txt", .{});
defer arquivo.close();
const writer = arquivo.writer();
try writer.writeAll("Primeira linha\n");
try writer.print("Valor: {d}\n", .{42});
try writer.print("Data: {s}\n", .{"2026-02-21"});
}
Append em arquivo existente
const std = @import("std");
pub fn main() !void {
const arquivo = try std.fs.cwd().openFile("log.txt", .{
.mode = .write_only,
});
defer arquivo.close();
// Ir para o final do arquivo
try arquivo.seekFromEnd(0);
const writer = arquivo.writer();
try writer.print("[INFO] Nova entrada de log\n", .{});
}
Buffered I/O
Buffers melhoram a performance reduzindo chamadas ao sistema:
const std = @import("std");
pub fn main() !void {
const arquivo = try std.fs.cwd().createFile("grande.txt", .{});
defer arquivo.close();
// Writer com buffer — muito mais rápido para muitas escritas pequenas
var buf_writer = std.io.bufferedWriter(arquivo.writer());
const writer = buf_writer.writer();
for (0..10000) |i| {
try writer.print("Linha {d}\n", .{i});
}
// IMPORTANTE: flush para garantir que tudo foi escrito
try buf_writer.flush();
}
const std = @import("std");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Diretório atual
const cwd = std.fs.cwd();
// Criar diretório
cwd.makeDir("novo_dir") catch |err| switch (err) {
error.PathAlreadyExists => {},
else => return err,
};
// Criar diretórios recursivamente
try cwd.makePath("a/b/c/d");
// Listar conteúdo de diretório
var dir = try cwd.openDir(".", .{ .iterate = true });
defer dir.close();
var iter = dir.iterate();
while (try iter.next()) |entrada| {
const tipo = switch (entrada.kind) {
.file => "arquivo",
.directory => "diretório",
.sym_link => "link simbólico",
else => "outro",
};
std.debug.print("{s}: {s}\n", .{ tipo, entrada.name });
}
// Caminhos absolutos
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
const caminho_abs = try cwd.realpath(".", &path_buf);
std.debug.print("Diretório atual: {s}\n", .{caminho_abs});
// Deletar arquivo
cwd.deleteFile("temp.txt") catch {};
// Deletar diretório vazio
cwd.deleteDir("novo_dir") catch {};
// Deletar diretório recursivamente
cwd.deleteTree("a") catch {};
// Renomear
try cwd.rename("antigo.txt", "novo.txt");
// Verificar se existe
_ = cwd.statFile("arquivo.txt") catch |err| {
if (err == error.FileNotFound) {
std.debug.print("Arquivo não existe\n", .{});
}
_ = allocator; // apenas para evitar unused
};
}
Caminhos (Paths)
const std = @import("std");
pub fn main() void {
const caminho = "/home/usuario/projeto/src/main.zig";
// Basename — nome do arquivo
const nome = std.fs.path.basename(caminho);
std.debug.print("Nome: {s}\n", .{nome}); // main.zig
// Dirname — diretório pai
const dir = std.fs.path.dirname(caminho);
std.debug.print("Dir: {s}\n", .{dir orelse "(nulo)"}); // /home/usuario/projeto/src
// Extensão
const ext = std.fs.path.extension(caminho);
std.debug.print("Ext: {s}\n", .{ext}); // .zig
// Stem — nome sem extensão
const stem = std.fs.path.stem(caminho);
std.debug.print("Stem: {s}\n", .{stem}); // main
}
Tabela de Referência — std.fs
| Operação | Método | Exemplo |
|---|
| Abrir arquivo | openFile(path, flags) | try cwd.openFile("a.txt", .{}) |
| Criar arquivo | createFile(path, flags) | try cwd.createFile("a.txt", .{}) |
| Ler tudo | readFileAlloc(alloc, path, max) | try cwd.readFileAlloc(alloc, "a.txt", 1e6) |
| Deletar arquivo | deleteFile(path) | try cwd.deleteFile("a.txt") |
| Criar diretório | makeDir(path) | try cwd.makeDir("dir") |
| Criar dirs recursivo | makePath(path) | try cwd.makePath("a/b/c") |
| Deletar diretório | deleteDir(path) | try cwd.deleteDir("dir") |
| Renomear | rename(old, new) | try cwd.rename("a", "b") |
| Stat | statFile(path) | try cwd.statFile("a.txt") |
Tabela de Referência — Reader/Writer
| Reader | Descrição |
|---|
readAll(buf) | Ler até encher o buffer |
readUntilDelimiterOrEof(buf, delim) | Ler até delimitador ou EOF |
readByte() | Ler um byte |
readInt(T, endian) | Ler inteiro com endianness |
skipBytes(n, .{}) | Pular N bytes |
| Writer | Descrição |
|---|
writeAll(bytes) | Escrever todos os bytes |
print(fmt, args) | Escrever formatado |
writeByte(byte) | Escrever um byte |
writeInt(T, val, endian) | Escrever inteiro com endianness |
Erros Comuns
// ERRO: Esquecer de fechar arquivo
// const f = try cwd.openFile("x.txt", .{});
// ... sem close()
// CORRETO: Sempre usar defer
const f = try std.fs.cwd().openFile("x.txt", .{});
defer f.close();
// ERRO: Esquecer flush do buffer
// buf_writer escreveu mas não fez flush
// CORRETO: Sempre fazer flush
try buf_writer.flush();
// ERRO: Buffer muito pequeno para readUntilDelimiterOrEof
// var buf: [10]u8 = undefined; // pode estourar
// CORRETO: buffer generoso ou usar allocator
Veja Também