Spec-Zone.ru › C++

Спецификация стандартного формата (начиная с C++20)

Для базовых типов и строковых типов спецификация формата основана на спецификации формата в Python.

Синтаксис спецификаций формата:

fill-and-align (необязательно) sign (необязательно) #(необязательно) 0(необязательно) width (необязательно) precision (необязательно) L(необязательно) type (необязательно)

Параметры sign, # и 0 допустимы только при использовании типа представления целого числа или числа с плавающей точкой.

Заполнение и выравнивание

fill-and-align — это необязательный символ заполнения (им может быть любой символ, кроме { или }), за которым следует один из параметров выравнивания <, >, ^.

Если символ заполнения не указан, по умолчанию используется пробел. В спецификации формата для кодировки Unicode символ заполнения должен соответствовать одному скалярному значению Unicode.

Параметры выравнивания имеют следующее значение:

  • <: принудительно выравнивает форматируемый аргумент по началу доступного пространства, вставляя n символов заполнения после форматируемого аргумента. Это значение используется по умолчанию, если применяется тип представления, отличный от целочисленного и типа с плавающей точкой.
  • >: принудительно выравнивает форматируемый аргумент по концу доступного пространства, вставляя n символов заполнения перед форматируемым аргументом. Это значение используется по умолчанию, если применяется тип представления целого числа или числа с плавающей точкой.
  • ^: принудительно выравнивает форматируемый аргумент по центру доступного пространства, вставляя ⌊n/2⌋ символов перед форматируемым аргументом и ⌈n/2⌉ символов после него.

Во всех случаях n — это разность между минимальной шириной поля (указанной с помощью width) и расчётной шириной форматируемого аргумента либо 0, если разность меньше 0.

#include <cassert>
#include <format>

int main()
{
    char c = 120;
    assert(std::format("{:6}", 42)    == "    42");
    assert(std::format("{:6}", 'x')   == "x     ");
    assert(std::format("{:*<6}", 'x') == "x*****");
    assert(std::format("{:*>6}", 'x') == "*****x");
    assert(std::format("{:*^6}", 'x') == "**x***");
    assert(std::format("{:6d}", c)    == "   120");
    assert(std::format("{:6}", true)  == "true  ");
}

Знак, # и 0

Параметр sign может принимать одно из следующих значений:

  • +: указывает, что знак следует использовать как для неотрицательных, так и для отрицательных чисел. Для неотрицательных чисел перед выходным значением вставляется знак +.
  • -: указывает, что знак следует использовать только для отрицательных чисел (такое поведение используется по умолчанию).
  • пробел: указывает, что для неотрицательных чисел следует использовать начальный пробел, а для отрицательных — знак минус.

Отрицательный ноль считается отрицательным числом.

Параметр sign применяется к бесконечности и NaN типа с плавающей точкой.

#include <cassert>
#include <format>
#include <limits>

int main()
{
    double inf = std::numeric_limits<double>::infinity();
    double nan = std::numeric_limits<double>::quiet_NaN();
    assert(std::format("{0:},{0:+},{0:-},{0: }", 1)   == "1,+1,1, 1");
    assert(std::format("{0:},{0:+},{0:-},{0: }", -1)  == "-1,-1,-1,-1");
    assert(std::format("{0:},{0:+},{0:-},{0: }", inf) == "inf,+inf,inf, inf");
    assert(std::format("{0:},{0:+},{0:-},{0: }", nan) == "nan,+nan,nan, nan");
}

Параметр # задаёт использование альтернативной формы при преобразовании.

  • Для целочисленных типов при использовании двоичного, восьмеричного или шестнадцатеричного типа представления альтернативная форма добавляет к выходному значению префикс (0b, 0 или 0x) после символа знака (возможно, пробела), если он есть, либо в противном случае добавляет его перед выходным значением.
  • Для типов с плавающей точкой альтернативная форма приводит к тому, что результат преобразования конечных значений всегда содержит символ десятичной точки, даже если за ним не следуют цифры. Обычно символ десятичной точки появляется в результате этих преобразований только в том случае, если за ним следует цифра. Кроме того, при преобразованиях g и G конечные нули не удаляются из результата.

Параметр 0 заполняет поле начальными нулями (после любого обозначения знака или основания) до ширины поля, за исключением случаев, когда он применяется к бесконечности или NaN. Если присутствуют символ 0 и параметр выравнивания, символ 0 игнорируется.

#include <cassert>
#include <format>

int main()
{
    char c = 120;
    assert(std::format("{:+06d}", c)   == "+00120");
    assert(std::format("{:#06x}", 0xa) == "0x000a");
    assert(std::format("{:<06}", -42)  == "-42   "); // 0 is ignored because of '<'
}

Ширина и точность

width — это положительное десятичное число или вложенное поле подстановки ({} или {n}). Если оно указано, то задаёт минимальную ширину поля.

precision — это точка (.), за которой следует неотрицательное десятичное число или вложенное поле подстановки. Это поле указывает точность или максимальный размер поля. Его можно использовать только с типами с плавающей точкой и строковыми типами.

  • Для типов с плавающей точкой это поле задаёт точность форматирования.
  • Для строковых типов оно задаёт верхнюю границу расчётной ширины (см. ниже) префикса строки, который будет скопирован в выходные данные. Для строки в кодировке Unicode в выходные данные копируется самый длинный префикс из целых расширенных графемных кластеров, расчётная ширина которого не превышает точность.

Если для width или precision используется вложенное поле подстановки, а соответствующий аргумент не имеет целочисленного типа(до C++23)стандартного знакового или беззнакового целочисленного типа(начиная с C++23) либо является отрицательным, выбрасывается исключение типа std::format_error.

float pi = 3.14f;
assert(std::format("{:10f}", pi)           == "  3.140000"); // width = 10
assert(std::format("{:{}f}", pi, 10)       == "  3.140000"); // width = 10
assert(std::format("{:.5f}", pi)           == "3.14000");    // precision = 5
assert(std::format("{:.{}f}", pi, 5)       == "3.14000");    // precision = 5
assert(std::format("{:10.5f}", pi)         == "   3.14000"); // width = 10, precision = 5
assert(std::format("{:{}.{}f}", pi, 10, 5) == "   3.14000"); // width = 10, precision = 5

auto b1 = std::format("{:{}f}", pi, 10.0); // throws: width is not of integral type
auto b2 = std::format("{:{}f}", pi, -10);  // throws: width is negative
auto b3 = std::format("{:.{}f}", pi, 5.0); // throws: precision is not of integral type

Ширина строки определяется как расчётное количество позиций столбцов, подходящее для её отображения в терминале.

При вычислении ширины предполагается, что строка представлена в кодировке, определяемой реализацией. Способ вычисления ширины не специфицирован, однако для строки в кодировке Unicode реализация должна оценивать ширину строки как сумму расчётных ширин первых кодовых точек её расширенных графемных кластеров. Расчётная ширина равна 2 для следующих кодовых точек и 1 для остальных:

  • Любая кодовая точка, свойство Unicode которой East_Asian_Width имеет значение Fullwidth (F) или Wide (W)
  • U+4DC0 - U+4DFF (символы гексаграмм И Цзин)
  • U+1F300 – U+1F5FF (различные символы и пиктограммы)
  • U+1F900 – U+1F9FF (дополнительные символы и пиктограммы)
#include <cassert>
#include <format>

int main()
{
    assert(std::format("{:.^5s}",   "🐱")    == ".🐱..");
    assert(std::format("{:.5s}",    "🐱🐱🐱") == "🐱🐱");
    assert(std::format("{:.<5.5s}", "🐱🐱🐱") == "🐱🐱.");
}

L (форматирование с учётом локали)

Параметр L задаёт использование формы с учётом локали. Этот параметр допустим только для арифметических типов.

  • Для целочисленных типов форма с учётом локали вставляет соответствующие разделители групп цифр согласно локали контекста.
  • Для типов с плавающей точкой форма с учётом локали вставляет соответствующие разделители групп цифр и десятичного разделителя согласно локали контекста.
  • Для текстового представления bool форма с учётом локали использует соответствующую строку, как если бы она была получена с помощью std::numpunct::truename или std::numpunct::falsename.

Тип

Параметр type определяет способ представления данных.

Доступны следующие типы представления строк:

  • none, s: копирует строку в выходные данные.
  • ?: копирует экранированную строку (см. ниже) в выходные данные.
(начиная с C++23)

Доступны следующие типы целочисленного представления для целочисленных типов, кроме char, wchar_t и bool:

  • b: двоичный формат. Формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, value, 2). Префикс основания — 0b.
  • B: то же, что b, за исключением того, что префикс основания — 0B.
  • c: копирует символ static_cast<CharT>(value) в выходные данные, где CharT — тип символа строки формата. Выбрасывает std::format_error, если значение не входит в диапазон представимых значений для CharT.
  • d: десятичный формат. Формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, value).
  • o: восьмеричный формат. Формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, value, 8). Префикс основания равен 0, если значение соответствующего аргумента отлично от нуля, и в противном случае является пустым.
  • x: шестнадцатеричный формат. Формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, value, 16). Префикс основания — 0x.
  • X: то же, что x, за исключением того, что для цифр больше 9 используются заглавные буквы, а префикс основания — 0X.
  • none: то же, что d.

Доступны следующие типы представления для char и wchar_t:

  • none, c: копирует символ в выходные данные.
  • b, B, d, o, x, X: использует типы целочисленного представления со значением static_cast<unsigned char>(value) или static_cast<std::make_unsigned_t<wchar_t>>(value) соответственно.
  • ?: копирует экранированный символ (см. ниже) в выходные данные.
(начиная с C++23)

Доступны следующие типы представления для bool:

  • none, s: копирует текстовое представление (true или false либо форму с учётом локали) в выходные данные.
  • b, B, d, o, x, X: использует типы целочисленного представления со значением static_cast<unsigned char>(value).

Доступны следующие типы представления для чисел с плавающей точкой:

  • a: если задана точность, формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, value, std::chars_format::hex, precision), где precision — заданная точность; в противном случае выходные данные формируются так, как если бы была вызвана функция std::to_chars(first, last, value, std::chars_format::hex).
  • A: то же, что a, за исключением того, что для цифр больше 9 используются заглавные буквы, а для обозначения экспоненты используется P.
  • e: формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, value, std::chars_format::scientific, precision), где precision — заданная точность или 6, если точность не задана.
  • E: то же, что e, за исключением того, что для обозначения экспоненты используется E.
  • f, F: формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, value, std::chars_format::fixed, precision), где precision — заданная точность или 6, если точность не задана.
  • g: формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, value, std::chars_format::general, precision), где precision — заданная точность или 6, если точность не задана.
  • G: то же, что g, за исключением того, что для обозначения экспоненты используется E.
  • none: если задана точность, формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, value, std::chars_format::general, precision), где precision — заданная точность; в противном случае выходные данные формируются так, как если бы была вызвана функция std::to_chars(first, last, value).

Для типов представления в нижнем регистре бесконечность и NaN форматируются соответственно как inf и nan. Для типов представления в верхнем регистре бесконечность и NaN форматируются соответственно как INF и NAN.

Доступны следующие типы представления указателей (также используемые для std::nullptr_t):

  • none, p: если определён std::uintptr_t, формирует выходные данные так, как если бы была вызвана функция std::to_chars(first, last, reinterpret_cast<std::uintptr_t>(value), 16), добавляя к выходным данным префикс 0x; в противном случае поведение определяется реализацией.
  • P: то же, что p, за исключением того, что для цифр больше 9 используются заглавные буквы, а префикс основания — 0X.
(начиная с C++26)

Форматирование экранированных символов и строк

Символ или строку можно форматировать в экранированном виде, чтобы сделать их более подходящими для отладки или ведения журналов.

Экранирование выполняется следующим образом:

  • Для каждой корректной последовательности кодовых единиц, кодирующей символ C:
    • Если C — один из символов в следующей таблице, используется соответствующая escape-последовательность.
Символ Escape-последовательность Примечания
табуляция (байт 0x09 в кодировке ASCII) \t
перевод строки — новая строка (байт 0x0a в кодировке ASCII) \n
возврат каретки (байт 0x0d в кодировке ASCII) \r
двойная кавычка (байт 0x22 в кодировке ASCII) \" Используется только в том случае, если выходные данные — строка в двойных кавычках
одинарная кавычка (байт 0x27 в кодировке ASCII) \' Используется только в том случае, если выходные данные — строка в одинарных кавычках
обратная косая черта (байт 0x5c в кодировке ASCII) \\
  • В противном случае, если C не является пробелом (байт 0x20 в кодировке ASCII) и выполняется одно из следующих условий:
  • связанная кодировка символов является кодировкой Unicode, и
    • C соответствует скалярному значению Unicode, свойство Unicode которого General_Category имеет значение из групп Separator (Z) или Other (C), либо
    • C не непосредственно предшествует неэкранированный символ, и C соответствует скалярному значению Unicode со свойством Unicode Grapheme_Extend=Yes, либо
  • связанная кодировка символов не является кодировкой Unicode, и C входит в определяемый реализацией набор разделителей или непечатаемых символов
используется escape-последовательность \u{hex-digit-sequence}, где hex-digit-sequence — кратчайшее шестнадцатеричное представление C с использованием строчных шестнадцатеричных цифр.
  • В противном случае C копируется без изменений.
  • Последовательность кодовых единиц, являющаяся последовательностью переключения, оказывает на выходные данные и дальнейшее декодирование строки неуказанное воздействие.
  • Все остальные кодовые единицы (то есть входящие в некорректные последовательности кодовых единиц) заменяются на \x{hex-digit-sequence}, где hex-digit-sequence — кратчайшее шестнадцатеричное представление кодовой единицы с использованием строчных шестнадцатеричных цифр.

Экранированное представление строки формируется путём экранирования последовательностей кодовых единиц в строке, как описано выше, и заключения результата в двойные кавычки.

Экранированное представление символа формируется путём его экранирования, как описано выше, и заключения результата в одинарные кавычки.

Демонстрация в Compiler Explorer:

#include <print>

int main()
{
    std::println("[{:?}]", "h\tllo");             // prints: ["h\tllo"]
    std::println("[{:?}]", "Спасибо, Виктор ♥!"); // prints: ["Спасибо, Виктор ♥!"]
    std::println("[{:?}] [{:?}]", '\'', '"');     // prints: ['\'', '"']

    // The following examples assume use of the UTF-8 encoding
    std::println("[{:?}]", std::string("\0 \n \t \x02 \x1b", 9));
                                             // prints: ["\u{0} \n \t \u{2} \u{1b}"]
    std::println("[{:?}]", "\xc3\x28");      // invalid UTF-8
                                             // prints: ["\x{c3}("]
    std::println("[{:?}]", "\u0301");        // prints: ["\u{301}"]
    std::println("[{:?}]", "\\\u0301");      // prints: ["\\\u{301}"]
    std::println("[{:?}]", "e\u0301\u0323"); // prints: ["ẹ́"]
}
(начиная с C++23)

Примечания

В большинстве случаев синтаксис похож на старое форматирование %, с добавлением {} и использованием : вместо %. Например, "%03.2f" можно преобразовать в "{:03.2f}".

Макрос проверки поддержки функции Значение Стандарт Функция
__cpp_lib_format_uchar 202311L (C++20)
(DR)
Форматирование кодовых единиц как беззнаковых целых чисел

Отчёты об ошибках

Следующие отчёты об ошибках, изменяющие поведение, были применены задним числом к ранее опубликованным стандартам C++.

DR Применён к Опубликованное поведение Корректное поведение
LWG 3721 C++20 ноль не допускается в поле ширины
в спецификации стандартного формата
ноль разрешён, если он указан
через поле подстановки
P2909R4 C++20 char или wchar_t могли форматироваться как
беззнаковые целочисленные значения вне допустимого диапазона
перед таким форматированием кодовые единицы преобразуются
в соответствующий беззнаковый тип

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/utility/format/spec

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API