Решил написать маленькую шпаргалку по форматированию значений при печати. А то, как что-то чуть сложнее {d} или {s}, приходится память напрягать, где же я про это читал. Теперь здесь буду читать, может кому-то еще пригодится.

Строка форматирования должна быть известна компилятору на этапе компиляции и может содержать заполнители в следующем формате:

{[аргумент][спецификатор]:[заполнение][выравнивание][ширина].[точность]}

Сформировать строку формата динамически увы не получится. Если нашли как, то поправьте меня в одном из наших чатов.

Препарируем формат

Аргумент

Аргумент — это либо числовой индекс, либо имя поля, которое нужно вставить в итоговую строку. При использовании имени поля необходимо заключить его (имя) в квадратные скобки, например {[score]...}, в отличие от числового индекса, который можно записать, например, как {2...}

Спецификатор

Спецификатор - символ, который определяет, как будет выводиться аргумент.

Спецификаторы:

  • x и X: вывод числового значения в шестнадцатеричной системе счисления или строки в шестнадцатеричных байтах;
  • s: печатает строку:
    • для указателей на множество и C-указателей на []u8 выведет в виде C-строки с завершающим нулём:
    • для срезов u8 выведет весь срез в виде строки без завершающего нуля;
  • t:
    • для перечислений и размеченных объединений: выводит имя тега;
    • для наборов ошибок: выводит имя ошибки;
  • b64: выведет строку в стандартном формате base64;
  • e: вывод значения с плавающей запятой в экспоненциальном представлении;
  • d: вывод десятичного числа;
  • b: вывод целочисленного значения в двоичной системе счисления;
  • o: вывод целочисленного значения в восьмеричной системе счисления;
  • c: вывод целого числа в виде символа ASCII. Целочисленный тип должен содержать максимум 8 бит (u8);
  • u: вывод целого числа в виде последовательности UTF-8. Целочисленный тип должен содержать максимум 21 бит (u21 и больше);
  • D: вывод наносекунд в качестве длительности;
  • B: выходные байты в единицах СИ (десятичных);
  • Bi: выходные байты в единицах IEC (двоичные);
  • ?: вывод необязательного значения либо в виде распакованного значения, либо как null. За ним может следовать спецификатор формата для базового значения;
  • !: вывод объединенного значения ошибки либо в виде развернутого значения, либо в виде отформатированного значения. За ним может следовать спецификатор формата для базового значения.
  • *: вывод адреса значения в памяти вместо самого значения;
  • any: вывод значения любого типа в формате по умолчанию;
  • f: делегирует вывод методу типа с именем «format» и сигнатурой
    fn (self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void
    

Заполнение

Заполнение — это один байт, который используется для заполнения отформатированных значений.

Выравнивание

Выравнивание — это один из трех байтов <, ^ или >, которые выравнивают числа по левому, центральному или правому краю соответственно.

Важно помнить, что:

  1. Не все спецификаторы поддерживают выравнивание.
  2. Выравнивание не учитывает кодировку Юникод и подходит только для работы с необработанными байтами или ASCII.

Ширина

Ширина — общая ширина поля в байтах для вывода значения. Это относится только к форматированию чисел.

Точность

Точность — количество знаков после запятой для вывода чисел с плавающей запятой.

Прочее

Чтобы напечатать фигурные скобки буквально, напишите их дважды, например {{ или }}.

Обратите внимание, что большинство параметров являются необязательными и могут быть опущены. Также можно не указывать разделители, такие как : и . если все параметры после разделителя опущены.

Единственное исключение — параметр заполнение. Если требуется указать ненулевой символ заполнения одновременно с шириной, необходимо также указать выравнивание, иначе цифра после : будет интерпретироваться как ширина, а не как символ заполнения.

Примеры

test-format.zig
const std = @import("std");

pub fn main() !void {
    const hour = 10;
    const minute = 30;
    const second = 45;
    const float_val = 125.2465;
    const str = "тест";
    const not_required: ?u8 = null;
    const err = error.OutOfMemory;
    const test_struct = TestStruct{ .hour = hour, .minute = minute, .second = second };

    // Вывод чисел с выравниванием. Для каждого числ выделяется область из 4 символов
    std.debug.print("1) {d: <4}:{d: ^4}:{d: >4}\n   ====:====:====\n", .{ hour, minute, second });

    // Вывод чисел с выравниванием. Но используются именованный и индексный доступ к переданным значениям
    std.debug.print("2) {[second]d: <4}:{[hour]d: ^4.2}:{1d: >4}\n", .{ .hour = hour, .minute = minute, .second = second });

    // Вывод текста в шестнадцатеричном формате
    std.debug.print("3) Текст \"{s}\" в шестнадцатеричном формате: 0x{0X} - 0x{0x}\n", .{str});

    // Вывод ошибки
    std.debug.print("4) Имя ошибки: {t}\n", .{err});

    // Вывод строки в формате Base64
    std.debug.print("5) Текст \"{s}\" в Base64: {0b64}\n", .{str});

    // Вывод чисел в различных форматах
    std.debug.print("6) Число {d} в экспоненциальном формате: {e}\n", .{ float_val, float_val });
    std.debug.print("7) Число {d} в двоичном формате: {b}\n", .{ second, second });
    std.debug.print("8) Число {d} в восьмеричном формате: {o}\n", .{ minute, minute });
    std.debug.print("9) Число {d} в символьном формате: {c}\n", .{ 0x74, 0x74 });
    std.debug.print("10) Число {d} в UTF-8 формате: {u}\n", .{ 0x10346, 0x10346 });
    std.debug.print("11) Число {d} в единицах СИ: {B}\n", .{ 125, 125 });
    std.debug.print("12) Число {d} в единицах IEC: {Bi}\n", .{ 1024, 1024 });

    // Вывод необязательного значения, которе может принимать числовое или значение null
    std.debug.print("13) Необязательное значение: {?d}\n", .{not_required});

    // Вывод структуры
    std.debug.print("14) Структура cо спецификатором \"any\": {any}\n", .{test_struct});

    // Делегирование вывода самой структуре
    std.debug.print("15) Делегирование вывода самой структуре: {f}\n", .{test_struct});
}

const TestStruct = struct {
    hour: u8,
    minute: u8,
    second: u8,

    pub fn format(self: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
        try writer.print("{d:0>2}:{d:0>2}:{d:0>2}", .{ self.hour, self.minute, self.second });
    }
};