# Formatting

### Formatting

Zig uses format strings to turn values into text.

The simplest form is `std.debug.print`.

```zig
const std = @import("std");

pub fn main() void {
    std.debug.print("{s} {d}\n", .{ "zig", 16 });
}
```

The output is:

```text
zig 16
```

The first argument is the format string.

```zig
"{s} {d}\n"
```

The second argument is a tuple of values.

```zig
.{ "zig", 16 }
```

Each placeholder in the format string consumes one value.

`{s}` prints string data.

`{d}` prints a decimal integer.

The format string is checked at compile time. If the string asks for a value in the wrong form, the compiler reports an error.

```zig
const std = @import("std");

pub fn main() void {
    std.debug.print("{d}\n", .{"hello"});
}
```

This is wrong. `{d}` expects a number.

Some common format specifiers are:

| Specifier | Meaning |
|---|---|
| `{}` | Default formatting |
| `{any}` | Debug formatting |
| `{s}` | String |
| `{d}` | Decimal integer |
| `{x}` | Lowercase hexadecimal |
| `{X}` | Uppercase hexadecimal |
| `{b}` | Binary |
| `{c}` | Character |

Example:

```zig
const std = @import("std");

pub fn main() void {
    const x: u8 = 255;

    std.debug.print("{}\n", .{x});
    std.debug.print("{d}\n", .{x});
    std.debug.print("{x}\n", .{x});
    std.debug.print("{X}\n", .{x});
    std.debug.print("{b}\n", .{x});
}
```

The output is:

```text
255
255
ff
FF
11111111
```

`{any}` is useful for inspecting values.

```zig
const std = @import("std");

const Point = struct {
    x: i32,
    y: i32,
};

pub fn main() void {
    const p = Point{ .x = 10, .y = 20 };

    std.debug.print("{any}\n", .{p});
}
```

The output is similar to:

```text
main.Point{ .x = 10, .y = 20 }
```

Formatting can also write to a buffer.

```zig
const std = @import("std");

pub fn main() !void {
    var buffer: [64]u8 = undefined;

    const text = try std.fmt.bufPrint(
        buffer[0..],
        "{s}-{d}",
        .{ "zig", 16 },
    );

    std.debug.print("{s}\n", .{text});
}
```

The output is:

```text
zig-16
```

`bufPrint` writes into the supplied buffer.

It returns a slice containing the written bytes.

```zig
[]u8
```

If the buffer is too small, it returns an error.

Formatting can also allocate a new string.

```zig
const std = @import("std");

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();

    const allocator = gpa.allocator();

    const text = try std.fmt.allocPrint(
        allocator,
        "{s}-{d}",
        .{ "zig", 16 },
    );
    defer allocator.free(text);

    std.debug.print("{s}\n", .{text});
}
```

`allocPrint` allocates enough memory for the formatted result.

The caller owns the returned slice and must free it.

```zig
defer allocator.free(text);
```

Formatting is therefore explicit in the same way allocation is explicit.

For fixed memory, use `bufPrint`.

For allocated memory, use `allocPrint`.

For diagnostic output, use `std.debug.print`.

Formatting also supports width and padding.

```zig
const std = @import("std");

pub fn main() void {
    std.debug.print("{d: >5}\n", .{42});
    std.debug.print("{d:0>5}\n", .{42});
}
```

The output is:

```text
   42
00042
```

The first line pads with spaces.

The second line pads with zeroes.

The width is five characters.

A useful rule is this: formatting does not hide ownership.

Printing writes somewhere.

Buffer formatting uses caller-provided memory.

Allocated formatting requires an allocator and returns memory the caller must free.

Exercise 14-17. Print the number `255` in decimal, hexadecimal, and binary.

Exercise 14-18. Format a string into a fixed buffer with `std.fmt.bufPrint`.

Exercise 14-19. Format a string with `std.fmt.allocPrint` and free it.

Exercise 14-20. Print several numbers in aligned columns.

