std.fmt — Formatação de Strings
O módulo std.fmt é o sistema central de formatação do Zig, usado por std.debug.print, writer.print, std.fmt.bufPrint e muitas outras funções da biblioteca padrão. Ele converte valores de qualquer tipo em representações textuais usando especificadores de formato em tempo de compilação, garantindo segurança e zero overhead em runtime.
Visão Geral
const std = @import("std");
const fmt = std.fmt;
O sistema de formatação do Zig é verificado em tempo de compilação: se o especificador de formato não corresponder ao tipo do argumento, o código não compila. Isso elimina toda uma classe de bugs comuns em C com printf.
Funções Principais
Formatação para Buffer
// Formata para um buffer fixo, retorna o slice escrito
pub fn bufPrint(buf: []u8, comptime format: []const u8, args: anytype) ![]u8
// Formata e aloca o resultado
pub fn allocPrint(allocator: Allocator, comptime format: []const u8, args: anytype) ![]u8
// Conta o número de bytes que seriam escritos
pub fn count(comptime format: []const u8, args: anytype) u64
Formatação para Writer
// Formata e escreve em qualquer Writer
pub fn format(comptime format: []const u8, args: anytype, writer: anytype) !void
Parsing de Formato
// Interpreta inteiros de strings
pub fn parseInt(comptime T: type, buf: []const u8, base: u8) !T
// Interpreta floats de strings
pub fn parseFloat(comptime T: type, buf: []const u8) !T
Especificadores de Formato
| Especificador | Tipo | Descrição |
|---|---|---|
{d} | inteiros | Decimal |
{x} | inteiros | Hexadecimal minúsculo |
{X} | inteiros | Hexadecimal maiúsculo |
{o} | inteiros | Octal |
{b} | inteiros | Binário |
{e} | floats | Notação científica |
{s} | []const u8 | String |
{c} | u8 | Caractere |
{any} | qualquer | Representação de debug |
{} | qualquer | Formato padrão |
Modificadores de Largura e Alinhamento
{d:>10} — alinhado à direita, largura 10
{d:<10} — alinhado à esquerda, largura 10
{d:^10} — centralizado, largura 10
{d:0>8} — preenchido com zeros, largura 8
{d:.3} — 3 casas decimais (para floats)
Exemplo 1: Formatação Básica com bufPrint
const std = @import("std");
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
// Inteiros em diferentes bases
const valor: u32 = 255;
try stdout.print("Decimal: {d}\n", .{valor});
try stdout.print("Hexadecimal: 0x{x:0>4}\n", .{valor});
try stdout.print("Binário: 0b{b:0>8}\n", .{valor});
try stdout.print("Octal: 0o{o}\n", .{valor});
// Floats
const pi: f64 = 3.14159265358979;
try stdout.print("Float: {d:.4}\n", .{pi});
try stdout.print("Científico: {e}\n", .{pi});
// Strings
try stdout.print("String: '{s}'\n", .{"Olá, Zig!"});
try stdout.print("Alinhado: |{s:>20}|\n", .{"direita"});
try stdout.print("Alinhado: |{s:<20}|\n", .{"esquerda"});
try stdout.print("Alinhado: |{s:^20}|\n", .{"centro"});
// bufPrint para buffer fixo
var buf: [128]u8 = undefined;
const resultado = try std.fmt.bufPrint(&buf, "Resultado: {d} + {d} = {d}", .{ 10, 20, 30 });
try stdout.print("{s}\n", .{resultado});
}
Exemplo 2: Formatação Customizada com format()
Tipos personalizados podem implementar format para controlar como são impressos:
const std = @import("std");
const Ponto = struct {
x: f32,
y: f32,
pub fn format(
self: @This(),
comptime fmt_str: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt_str;
_ = options;
try writer.print("({d:.2}, {d:.2})", .{ self.x, self.y });
}
};
const Cor = struct {
r: u8,
g: u8,
b: u8,
pub fn format(
self: @This(),
comptime fmt_str: []const u8,
options: std.fmt.FormatOptions,
writer: anytype,
) !void {
_ = fmt_str;
_ = options;
try writer.print("#{x:0>2}{x:0>2}{x:0>2}", .{ self.r, self.g, self.b });
}
};
pub fn main() !void {
const stdout = std.io.getStdOut().writer();
const p = Ponto{ .x = 3.14, .y = 2.71 };
const c = Cor{ .r = 255, .g = 128, .b = 0 };
try stdout.print("Ponto: {}\n", .{p}); // (3.14, 2.71)
try stdout.print("Cor: {}\n", .{c}); // #ff8000
}
Exemplo 3: allocPrint e Construção Dinâmica de Strings
const std = @import("std");
fn gerarSQL(
allocator: std.mem.Allocator,
tabela: []const u8,
campos: []const []const u8,
limite: u32,
) ![]u8 {
var buf = std.ArrayList(u8).init(allocator);
defer buf.deinit();
const writer = buf.writer();
try writer.print("SELECT ", .{});
for (campos, 0..) |campo, i| {
if (i > 0) try writer.writeAll(", ");
try writer.writeAll(campo);
}
try writer.print(" FROM {s} LIMIT {d}", .{ tabela, limite });
return try buf.toOwnedSlice();
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// allocPrint simples
const saudacao = try std.fmt.allocPrint(allocator, "Olá, {s}! Hoje é dia {d}.", .{
"Maria", 21,
});
defer allocator.free(saudacao);
const stdout = std.io.getStdOut().writer();
try stdout.print("{s}\n", .{saudacao});
// Construção de SQL
const campos = [_][]const u8{ "id", "nome", "email" };
const sql = try gerarSQL(allocator, "usuarios", &campos, 100);
defer allocator.free(sql);
try stdout.print("SQL: {s}\n", .{sql});
}
Padrões Comuns
Formatação com Comptime
A string de formato deve ser comptime, o que permite verificação completa em tempo de compilação:
// Isso NÃO compila — tipo errado para {d}
// std.debug.print("{d}", .{"texto"});
// Isso compila corretamente
std.debug.print("{s}", .{"texto"});
Debug de Qualquer Tipo
Use {any} para imprimir qualquer valor, incluindo structs complexas:
const dados = .{ .x = 1, .y = "teste", .z = true };
std.debug.print("{any}\n", .{dados});
Módulos Relacionados
- std.fmt Format Strings — Sintaxe detalhada de format strings
- std.fmt.parseInt — Parsing de inteiros e floats
- std.io.Writer — Interface Writer que usa fmt
- std.mem — Operações de memória