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
- std.fs — Visão geral do sistema de arquivos
- std.fs.cwd() — Diretório de trabalho atual
- std.fs.Dir — Tipo Dir e iteração
- std.fs.File — Tipo File
- std.mem — Funções de memória para manipular slices