std.fmt em Zig — Referência e Exemplos

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

EspecificadorTipoDescrição
{d}inteirosDecimal
{x}inteirosHexadecimal minúsculo
{X}inteirosHexadecimal maiúsculo
{o}inteirosOctal
{b}inteirosBinário
{e}floatsNotação científica
{s}[]const u8String
{c}u8Caractere
{any}qualquerRepresentação de debug
{}qualquerFormato 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

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

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