⚡ Zig Guide LiveUnofficial
✓ Zig 0.17.0-dev.1454+5faa79730On an older Zig?

Formatting

const std = @import("std");
const expect = std.testing.expect;

test "bufPrint formats into memory you own" {
    var buf: [64]u8 = undefined;
    const text = try std.fmt.bufPrint(&buf, "{s} is {d}", .{ "answer", 42 });
    try expect(std.mem.eql(u8, text, "answer is 42"));
}

test "common specifiers" {
    var buf: [64]u8 = undefined;

    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{d}", .{255}), "255"));
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{x}", .{255}), "ff"));
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{X}", .{255}), "FF"));
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{b}", .{5}), "101"));
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{o}", .{8}), "10"));
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{c}", .{@as(u8, 'A')}), "A"));
}

test "width, alignment and fill" {
    var buf: [64]u8 = undefined;

    // {[fill][align][width]}: align is <, ^ or >.
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{d:5}", .{42}), "   42"));
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{d:<5}", .{42}), "42   "));
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{d:^5}", .{42}), " 42  "));
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{d:0>5}", .{42}), "00042"));
}

test "float precision" {
    var buf: [64]u8 = undefined;
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{d:.2}", .{3.14159}), "3.14"));
}

test "escaping braces" {
    var buf: [64]u8 = undefined;
    try expect(std.mem.eql(u8, try std.fmt.bufPrint(&buf, "{{{d}}}", .{1}), "{1}"));
}

test "allocPrint when the length is unknown" {
    const gpa = std.testing.allocator;
    const text = try std.fmt.allocPrint(gpa, "{d}-{d}", .{ 1, 2 });
    defer gpa.free(text);
    try expect(std.mem.eql(u8, text, "1-2"));
}

Where the output goes

FunctionWrites to
bufPrinta buffer you supply (no allocation)
allocPrintfresh memory (caller frees)
writer.printany writer

bufPrint returns a slice of the part it filled, and fails with error.NoSpaceLeft rather than truncating.

Specifiers

SpecMeaning
{d}decimal
{x} / {X}lower / upper hex
{b} / {o}binary / octal
{s}string
{c}a byte as a character
{any}structural dump

Width, alignment, precision

The form is {[fill][align][width]}, with < ^ > for alignment:

"{d:5}"     // "   42"  (right by default for numbers)
"{d:<5}"    // "42   "
"{d:^5}"    // " 42  "
"{d:0>5}"   // "00042"
"{d:.2}"    // "3.14"

Escape a literal brace by doubling it: {{ and }}.

Checked at compile time

The format string is a comptime parameter, so the argument count and each specifier are validated against the argument types during compilation. A {d} pointed at a string does not compile. There is no runtime format-string failure mode to worry about.