Formatting
std.fmt
provides
ways to format data to and from strings.
A basic example of creating a formatted string. The format string must be
compile-time known. The d
here denotes that we want a decimal number.
4 collapsed lines
const std = @import("std");const expect = std.testing.expect;const eql = std.mem.eql;
const test_allocator = std.testing.allocator;
test "fmt" { const string = try std.fmt.allocPrint( test_allocator, "{d} + {d} = {d}", .{ 9, 10, 19 }, ); defer test_allocator.free(string);
try expect(eql(u8, string, "9 + 10 = 19"));}
Writers conveniently have a print
method, which works similarly.
5 collapsed lines
const std = @import("std");const expect = std.testing.expect;const eql = std.mem.eql;const test_allocator = std.testing.allocator;
test "print" { var list = std.ArrayList(u8).init(test_allocator); defer list.deinit(); try list.writer().print( "{} + {} = {}", .{ 9, 10, 19 }, ); try expect(eql(u8, list.items, "9 + 10 = 19"));}
Take a moment to appreciate that you now know from top to bottom how printing
Hello World works.
std.debug.print
works the same, except it writes to stderr and is protected by a mutex.
4 collapsed lines
const std = @import("std");const expect = std.testing.expect;const eql = std.mem.eql;
test "hello world" { const out_file = std.io.getStdOut(); try out_file.writer().print( "Hello, {s}!\n", .{"World"}, );}
We have used the {s}
format specifier up until this point to print strings.
Here, we will use {any}
, which gives us the default formatting.
5 collapsed lines
const std = @import("std");const expect = std.testing.expect;const eql = std.mem.eql;const test_allocator = std.testing.allocator;
test "array printing" { const string = try std.fmt.allocPrint( test_allocator, "{any} + {any} = {any}", .{ @as([]const u8, &[_]u8{ 1, 4 }), @as([]const u8, &[_]u8{ 2, 5 }), @as([]const u8, &[_]u8{ 3, 9 }), }, ); defer test_allocator.free(string);
try expect(eql( u8, string, "{ 1, 4 } + { 2, 5 } = { 3, 9 }", ));}
Let’s create a type with custom formatting by giving it a format
function.
This function must be marked as pub
so that std.fmt can access it (more on
packages later). You may notice the usage of {s}
instead of {}
- this is the
format specifier for strings (more on format specifiers later). This is used
here as {}
defaults to array printing over string printing.
5 collapsed lines
const std = @import("std");const expect = std.testing.expect;const eql = std.mem.eql;const test_allocator = std.testing.allocator;
const Person = struct { name: []const u8, birth_year: i32, death_year: ?i32, pub fn format( self: Person, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype, ) !void { _ = fmt; _ = options;
try writer.print("{s} ({}-", .{ self.name, self.birth_year, });
if (self.death_year) |year| { try writer.print("{}", .{year}); }
try writer.writeAll(")"); }};
test "custom fmt" { const john = Person{ .name = "John Carmack", .birth_year = 1970, .death_year = null, };
const john_string = try std.fmt.allocPrint( test_allocator, "{s}", .{john}, ); defer test_allocator.free(john_string);
try expect(eql( u8, john_string, "John Carmack (1970-)", ));
const claude = Person{ .name = "Claude Shannon", .birth_year = 1916, .death_year = 2001, };
const claude_string = try std.fmt.allocPrint( test_allocator, "{s}", .{claude}, ); defer test_allocator.free(claude_string);
try expect(eql( u8, claude_string, "Claude Shannon (1916-2001)", ));}