std.fs.path em Zig — Referência e Exemplos

std.fs.path — Utilitários de Caminhos

O submódulo std.fs.path fornece funções para manipulação de caminhos de arquivo de forma portável. Ele lida com as diferenças entre separadores de caminho em diferentes sistemas operacionais (ex: / no Linux/macOS vs \ no Windows).

Visão Geral

const std = @import("std");
const path = std.fs.path;

As funções de std.fs.path trabalham com slices de bytes ([]const u8) e não fazem alocação de memória, exceto quando explicitamente indicado (funções com sufixo Alloc).

Constantes

pub const sep: u8 = '/';         // Separador do sistema (ou '\\' no Windows)
pub const sep_str: []const u8 = "/";
pub const delimiter: u8 = ':';   // Delimitador de PATH (ou ';' no Windows)
pub const max_path_bytes: usize = 4096;

Funções Principais

Decomposição de Caminhos

// Retorna o diretório pai: "/home/user/file.txt" -> "/home/user"
pub fn dirname(path: []const u8) ?[]const u8

// Retorna o nome do arquivo: "/home/user/file.txt" -> "file.txt"
pub fn basename(path: []const u8) []const u8

// Retorna a extensão: "arquivo.tar.gz" -> ".gz"
pub fn extension(path: []const u8) []const u8

// Retorna o nome sem extensão: "arquivo.tar.gz" -> "arquivo.tar"
pub fn stem(path: []const u8) []const u8

Construção de Caminhos

// Junta componentes de caminho com o separador do sistema
pub fn join(allocator: Allocator, paths: []const []const u8) ![]u8

// Resolve um caminho relativo a partir de uma base
pub fn resolve(allocator: Allocator, paths: []const []const u8) ![]u8

Análise de Caminhos

// Verifica se é caminho absoluto
pub fn isAbsolute(path: []const u8) bool

// Calcula o caminho relativo entre dois caminhos
pub fn relative(allocator: Allocator, from: []const u8, to: []const u8) ![]u8

Exemplo 1: Decomposição de Caminhos

const std = @import("std");
const path = std.fs.path;

pub fn main() void {
    const caminho = "/home/usuario/projetos/app/src/main.zig";

    // Diretório pai
    if (path.dirname(caminho)) |dir| {
        std.debug.print("Diretório: {s}\n", .{dir});
        // /home/usuario/projetos/app/src
    }

    // Nome do arquivo
    const nome = path.basename(caminho);
    std.debug.print("Nome: {s}\n", .{nome}); // main.zig

    // Extensão
    const ext = path.extension(caminho);
    std.debug.print("Extensão: {s}\n", .{ext}); // .zig

    // Nome sem extensão (stem)
    const stem = path.stem(caminho);
    std.debug.print("Stem: {s}\n", .{stem}); // main

    // Verificar se é absoluto
    std.debug.print("Absoluto: {}\n", .{path.isAbsolute(caminho)}); // true
    std.debug.print("Absoluto: {}\n", .{path.isAbsolute("src/main.zig")}); // false

    // Extensões compostas
    const arquivo_tar = "backup.tar.gz";
    std.debug.print("Ext de '{s}': {s}\n", .{ arquivo_tar, path.extension(arquivo_tar) }); // .gz
    std.debug.print("Stem de '{s}': {s}\n", .{ arquivo_tar, path.stem(arquivo_tar) }); // backup.tar
}

Exemplo 2: Construção de Caminhos com join

const std = @import("std");
const path = std.fs.path;

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

    // Juntar componentes
    const caminho1 = try path.join(allocator, &.{ "/home", "usuario", "documentos" });
    defer allocator.free(caminho1);
    std.debug.print("Caminho 1: {s}\n", .{caminho1});
    // /home/usuario/documentos

    // Juntar com arquivo
    const caminho2 = try path.join(allocator, &.{ "/var", "log", "app", "output.log" });
    defer allocator.free(caminho2);
    std.debug.print("Caminho 2: {s}\n", .{caminho2});
    // /var/log/app/output.log

    // Se um componente é absoluto, descarta os anteriores
    const caminho3 = try path.join(allocator, &.{ "/home/user", "/etc/config" });
    defer allocator.free(caminho3);
    std.debug.print("Caminho 3: {s}\n", .{caminho3});
    // /etc/config (componente absoluto substitui o anterior)
}

Exemplo 3: Processamento de Lista de Arquivos

const std = @import("std");
const path = std.fs.path;

const ArquivoInfo = struct {
    diretorio: ?[]const u8,
    nome: []const u8,
    extensao: []const u8,
};

fn analisarCaminho(caminho: []const u8) ArquivoInfo {
    return .{
        .diretorio = path.dirname(caminho),
        .nome = path.stem(caminho),
        .extensao = path.extension(caminho),
    };
}

pub fn main() void {
    const arquivos = [_][]const u8{
        "/home/user/foto.jpg",
        "/tmp/dados.csv",
        "relatorio.pdf",
        "/var/log/sistema.log.1",
        "src/lib/utils.zig",
    };

    std.debug.print("{s:<35} {s:<15} {s:<12} {s}\n", .{
        "Caminho", "Nome", "Extensão", "Diretório",
    });
    std.debug.print("{s}\n", .{"-" ** 75});

    for (arquivos) |arq| {
        const info = analisarCaminho(path.basename(arq));
        std.debug.print("{s:<35} {s:<15} {s:<12} {s}\n", .{
            arq,
            info.nome,
            if (info.extensao.len > 0) info.extensao else "(nenhuma)",
            path.dirname(arq) orelse ".",
        });
    }
}

Padrões Comuns

Trocar Extensão de Arquivo

fn trocarExtensao(allocator: Allocator, caminho: []const u8, nova_ext: []const u8) ![]u8 {
    const sem_ext = path.stem(caminho);
    const dir = path.dirname(caminho);

    if (dir) |d| {
        return try std.fmt.allocPrint(allocator, "{s}/{s}{s}", .{ d, sem_ext, nova_ext });
    } else {
        return try std.fmt.allocPrint(allocator, "{s}{s}", .{ sem_ext, nova_ext });
    }
}

// "foto.jpg" -> "foto.png"
// "/home/user/dados.csv" -> "/home/user/dados.json"

Normalização de Caminho

Para resolver . e .. em caminhos:

const resolvido = try path.resolve(allocator, &.{ "/home/user/../admin/./docs" });
defer allocator.free(resolvido);
// /home/admin/docs

Iteração sobre Componentes

var iter = std.mem.splitScalar(u8, caminho, std.fs.path.sep);
while (iter.next()) |componente| {
    if (componente.len > 0) {
        std.debug.print("Componente: {s}\n", .{componente});
    }
}

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

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