Data formatting in Zig

The program demonstrates various advanced string formatting capabilities provided by the Standard Library std.fmt module.

const std = @import("std");

const User = struct {
    name: []const u8,
    id: u32,
};

pub fn main(init: std.process.Init) !void {
    const io = init.io;
    const allocator = std.heap.page_allocator;
    const stdout = std.Io.File.stdout();
    std.debug.print("Debug: Program started.\n", .{});

    const number = 255;
    const msg_nums = try std.fmt.allocPrint(
        allocator,
        "Decimal: {d}, Hex: {x}, Binary: {b}, Octal: {o}\n",
        .{ number, number, number, number },
    );
    defer allocator.free(msg_nums);
    try stdout.writeStreamingAll(io, msg_nums);

    const pi = 3.14159;
    const msg_float = try std.fmt.allocPrint(
        allocator,
        "Pi (2 decimals): {d:.2} | Padded: {d: >10.2}\n",
        .{ pi, pi },
    );
    defer allocator.free(msg_float);
    try stdout.writeStreamingAll(io, msg_float);

    const user = User{ .name = "Alice", .id = 101 };
    const numbers = [_]u8{ 1, 2, 3 };

    const msg_struct = try std.fmt.allocPrint(
        allocator,
        "User: {any}, Array: {any}\n",
        .{ user, numbers },
    );
    defer allocator.free(msg_struct);
    try stdout.writeStreamingAll(io, msg_struct);

    var buffer: [64]u8 = undefined;
    const slice = try std.fmt.bufPrint(
        &buffer,
        "Stack buffer: {s}-{d}",
        .{ "Data", 42 },
    );
    try stdout.writeStreamingAll(io, slice);
    try stdout.writeStreamingAll(io, "\n");
}

The code starts by importing the standard library and defining a simple User struct that holds a name as a string slice and an id as a u32. In the main function, the program initializes I/O handling, uses the page allocator for memory management (noted as a convenience for this example), and gets a handle to standard output. A debug message is printed first to indicate the program has started.

The code then shows numeric formatting for the integer 255. Using std.fmt.allocPrint(), it creates a formatted string displaying the number in decimal ({d}), hexadecimal ({x}), binary ({b}), and octal ({o}) formats. This dynamically allocates memory on the heap, writes the result to stdout using streaming output, and safely frees the memory afterward with defer.

Next, the program illustrates floating-point formatting with the value of π (3.14159). It formats the number to two decimal places and also demonstrates padding by right-aligning it within a 10-character field using the specifier {d: >10.2}. This again uses heap allocation via allocPrint followed by proper cleanup.

The example continues by formatting more complex types: a User struct instance (“Alice” with id 101) and a fixed-size array of bytes [1, 2, 3]. The {any} format specifier is used to recursively format these structures, producing readable output that includes the struct fields and array contents.

Finally, to avoid any heap allocation, the program uses a fixed-size stack buffer of 64 bytes with std.fmt.bufPrint() to format a simple message combining a string and an integer. The resulting slice is written directly to standard output, followed by a newline.

Save it as stdFmt.zig and execute it:

$ zig version
0.16.0
$ zig run stdFmt.zig
Debug: Program started.
Decimal: 255, Hex: ff, Binary: 11111111, Octal: 377
Pi (2 decimals): 3.14 | Padded:       3.14
User: .{ .name = { 65, 108, 105, 99, 101 }, .id = 101 }, Array: { 1, 2, 3 }
Stack buffer: Data-42

Happy coding in Zig!