Como Trabalhar com Arquivos Temporários em Zig

Introdução

Arquivos temporários são essenciais para armazenar dados intermediários durante processamento, testes e operações que precisam de espaço temporário em disco. Zig permite criar arquivos e diretórios temporários de forma segura, com controle explícito sobre a limpeza.

Nesta receita, você aprenderá a criar, usar e limpar arquivos e diretórios temporários.

Pré-requisitos

Criar e Escrever em Arquivo Temporário

const std = @import("std");

pub fn main() !void {
    // Obter diretório temporário do sistema
    const tmp_dir = std.fs.cwd();

    // Criar arquivo temporário
    const tmp_file = try tmp_dir.createFile("temp_dados.tmp", .{});
    defer {
        tmp_file.close();
        // Limpar: deletar o arquivo temporário
        tmp_dir.deleteFile("temp_dados.tmp") catch {};
    }

    // Escrever dados
    const writer = tmp_file.writer();
    try writer.writeAll("Dados temporários linha 1\n");
    try writer.writeAll("Dados temporários linha 2\n");
    try writer.print("Timestamp: {d}\n", .{std.time.timestamp()});

    std.debug.print("Arquivo temporário criado e escrito com sucesso\n", .{});

    // Voltar ao início para ler
    try tmp_file.seekTo(0);

    // Ler de volta
    var buf: [256]u8 = undefined;
    const bytes = try tmp_file.readAll(&buf);
    std.debug.print("Conteúdo ({d} bytes):\n{s}\n", .{ bytes, buf[0..bytes] });
}

Usar Diretório Temporário do Sistema

const std = @import("std");

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

    // Obter path do diretório temporário
    const tmp_path = try std.fs.selfExeDirPathAlloc(allocator);
    defer allocator.free(tmp_path);

    std.debug.print("Diretório do executável: {s}\n", .{tmp_path});

    // Usar /tmp diretamente (Linux)
    const tmp = try std.fs.openDirAbsolute("/tmp", .{});

    // Criar um arquivo com nome único
    const pid = std.os.linux.getpid();
    const nome = try std.fmt.allocPrint(allocator, "zig_temp_{d}.txt", .{pid});
    defer allocator.free(nome);

    const arquivo = try tmp.createFile(nome, .{});
    defer {
        arquivo.close();
        tmp.deleteFile(nome) catch {};
    }

    try arquivo.writer().print("Arquivo temporário do processo {d}\n", .{pid});

    std.debug.print("Criado /tmp/{s}\n", .{nome});
}

Criar Diretório Temporário

const std = @import("std");

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

    const nome_dir = "zig_temp_dir";

    // Criar diretório temporário
    const cwd = std.fs.cwd();
    cwd.makeDir(nome_dir) catch |err| {
        if (err != error.PathAlreadyExists) return err;
    };

    defer {
        // Limpar diretório e conteúdo
        cwd.deleteTree(nome_dir) catch |err| {
            std.debug.print("Erro ao limpar: {}\n", .{err});
        };
    }

    // Abrir o diretório
    var dir = try cwd.openDir(nome_dir, .{});
    defer dir.close();

    // Criar arquivos dentro do diretório temporário
    for (0..3) |i| {
        const nome = try std.fmt.allocPrint(allocator, "dados_{d}.txt", .{i});
        defer allocator.free(nome);

        const arquivo = try dir.createFile(nome, .{});
        defer arquivo.close();

        try arquivo.writer().print("Arquivo {d}\n", .{i});
        std.debug.print("Criado {s}/{s}\n", .{ nome_dir, nome });
    }

    // Listar conteúdo
    std.debug.print("\nConteúdo do diretório temporário:\n", .{});
    var iter = dir.iterate();
    while (try iter.next()) |entry| {
        std.debug.print("  {s} ({s})\n", .{ entry.name, @tagName(entry.kind) });
    }
}

Padrão: Processamento com Arquivo Temporário

Escrever resultado em arquivo temporário, depois renomear para destino final (atômico):

const std = @import("std");

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

    const destino = "resultado_final.txt";
    const tmp_nome = "resultado_final.txt.tmp";

    const cwd = std.fs.cwd();

    // Escrever em arquivo temporário
    {
        const tmp = try cwd.createFile(tmp_nome, .{});
        defer tmp.close();

        const writer = tmp.writer();

        // Processar e escrever dados
        for (0..10) |i| {
            const linha = try std.fmt.allocPrint(allocator, "Resultado {d}: {d}\n", .{ i, i * i });
            defer allocator.free(linha);
            try writer.writeAll(linha);
        }
    }

    // Renomear atomicamente (garante integridade)
    cwd.rename(tmp_nome, destino) catch |err| {
        std.debug.print("Erro ao renomear: {}\n", .{err});
        // Limpar arquivo temporário em caso de falha
        cwd.deleteFile(tmp_nome) catch {};
        return err;
    };

    std.debug.print("Arquivo '{s}' criado com sucesso\n", .{destino});

    // Verificar resultado
    const conteudo = try cwd.readFileAlloc(allocator, destino, 4096);
    defer allocator.free(conteudo);
    std.debug.print("Conteúdo:\n{s}", .{conteudo});

    // Limpar
    try cwd.deleteFile(destino);
}

Dicas e Boas Práticas

  1. Sempre limpe arquivos temporários: Use defer para garantir que arquivos e diretórios temporários sejam removidos.

  2. Nomes únicos: Use PID, timestamp ou UUID para evitar conflitos entre processos.

  3. Escrita atômica: Escreva em arquivo temporário e renomeie para o destino final para evitar corrupção.

  4. deleteTree para diretórios: Remove diretório e todo seu conteúdo recursivamente.

  5. Permissões: Arquivos em /tmp são acessíveis por outros processos. Use permissões restritivas para dados sensíveis.

Receitas Relacionadas

Tutoriais Relacionados

Continue aprendendo Zig

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