Для более удобного форматирования строк начиная со стандарта C++20 в стандартную библиотеку языка C++ добавлен модуль format
и в частности функция std::format(). В качестве первого
аргумента функция принимает строку форматирования. Эта строка содержит любое количество плейсхолдеров {}
. Второй и последующие параметры представляют
аргументы, которые вставляются в эти плейсхолдеры - внутрь фигурных скобок - по одному аргументу для каждой пары фигурных скобок.
Рассмотрим небольшой пример:
#include <iostream> #include <format> int main() { int a {10}; int b {7}; std::cout << std::format("{} + {} = {}", a, b, a+b); }
Здесь строка форматирования содержит три плейсхолдера {}
: "{} + {} = {}"
. В качестве второго, третьего и четвертого параметра - передаются значения,
которые будут вставляться в плейсхолдеры в порядке следования: первое значения вставляет в первую пару фигурных скобок, второе значение - во вторую пару и так далее. В итоге
мы получим следующий консольный вывод:
10 + 7 = 17
Стоит отметить, что поддержка этой функции в виду ее недавного добавления в стандарт в зависимости от компилятора может отличаться. Так, Visual Studio полностью поддерживает функцию,
а в GCC(g++) поддержка была добавлена только начиная с версии 13.0. А при компиляции с помощью Clang может потребоваться добавить флаг -fexperimental-library
clang++ -std=c++20 -fexperimental-library hello.cpp -o hello
Каждый плейсхолдер может содержать различные настройки в следующим виде:
[[fill]align][sign][#][0][width][.precision][type]
В квадратных скобках указаны отдельные параметры форматирования. Все эти параметры применяются к различным типам. Рассмотрим некоторые из них.
Спецификатор формата позволяет установить количество отображаемых десятичных знаков числа с плавающей запятой и количество отображаемых символов строки. Он имеет следующий формат:
:.количество_знаков
После двоеточия и точки указывается количество знаков отображаемого числа:
#include <iostream> #include <format> int main() { double sum {100.2567}; std::cout << std::format("sum = {:.5}", sum); }
В данном случае форматирование применяется к числу sum. Оно равно 100.2567
. В строку форматирования передается спецификатор :.5
, соответственно при выводе на консоль
отображаться будут только первые 5 цифр числа:
sum = 100.26
Стоит отметить, что последняя отображаемая цифра увеличивается на 1, если предыдущая цифра, которая отобрасывается при форматировании, больше или равна 5.
Поэтому в данном случае вместо 100.25
консоль выводит 100.26
По умолчанию значение спецификатора формата указывает на общее количество значащих цифр (в нашем примере 5), в том числе цифры как до, так и после точки. Но также можно в качестве спецификатора указать количество цифр после запятой. Для этого идобавляется букву f к спецификатору формата:
std::cout << std::format("sum = {:.5f}", sum);
Здесь опять же выводим пять знаков, но уже после запятой. Однако поскольку число sum в дробной части имеет только 4 знака, то в качестве пятого знака применяется 0:
sum = 100.25670
Аналогично с помощью данного спецификатора можно задать количество отображаемых символов строки. Например, отобразим только пять символов строки:
std::cout << std::format("name = {:.5}", "Tom Smith"); // name = Tom S
Параметр width
позволяет задать минимальную ширину поля. Чтобы достичь этой минимальной ширины в отформатированное значение при необходимости вставляются
дополнительные символы - символы заполнения. Какие символы вставляются - зависит
от типа значения и других параметров форматирования. Для явной установки символа заполнения применяется параметр fill
, который идет перед параметром width
.
Например, если параметру ширины предшествует символ "0" (ноль), то перед числом вставляются
дополнительные нули. Если символ заполнения явным образом не указан, то вставляется так называемый символ заполнения по умолчанию.
#include <iostream> #include <format> int main() { int a {2}; int b {5}; int c {-8}; std::cout << std::format("a = {:07}", a) << std::endl; std::cout << std::format("b = {:7}", b) << std::endl; std::cout << std::format("c = {:07}", c) << std::endl; }
В данном случае выводятся три числа, для каждого из которых устанавливается минимальная длина в 7 символов. Однако для первого и третьего чисел в качестве символа заполнения применяется "0", а для второго числа - символ заполнения по умолчанию (пробел). Консольный вывод:
a = 0000002 b = 5 c = -000008
Если для числа указан знак (+ или -), то символы заполнения вставляются после знака.
Параметр align
определяет, как выравнивается форматируемое значение: по левому краю (<),
по правому краю (>) или по центру (^). Выравнивание по умолчанию зависит от типа значения.
#include <iostream> #include <format> int main() { std::cout << std::format("{:>7}|{:>7}|{:>7}|{:>7}|\n", 1, -.2, 3, 4); std::cout << std::format("{:<7}|{:<7}|{:<7}|{:<7}|\n", 1, -.2, 3, 4); std::cout << std::format("{:^7}|{:^7}|{:^7}|{:^7}|\n", 1, -.2, 3, 4); }
Консольный вывод:
1| -0.2| 3| 4| 1 |-0.2 |3 |4 | 1 | -0.2 | 3 | 4 |
Параметр type
задает тип форматирования (не тип данных). Конкретный тип зависит от типа данных значения.
Для чисел с плавающей точкой применяется типы:
f: форматирование с фиксированной точкой
g: общее форматирование
e: экспоненциальная запись
a: шестнадцатеричная запись
Для целых чисел добавляются типы:
b: вывод числа в двоичном формате
x: вывод числа в шестнадцатеричном формате
Применение типов:
#include <iostream> #include <format> int main() { std::cout << std::format("{:f} \n", 34.56); // 34.560000 std::cout << std::format("{:g} \n", 34.56); // 34.56 std::cout << std::format("{:e} \n", 34.56); // 3.456000e+01 std::cout << std::format("{:a} \n", 34.56); // 1.147ae147ae148p+5 std::cout << std::format("{:b} \n", 122); // 1111010 std::cout << std::format("{:032b} \n", 122); // 00000000000000000000000001111010 std::cout << std::format("{:x} \n", 122); // 7A }
По умолчанию аргументы передаются в строку форматирования в порядке следования. Но мы можем переопределить вывод, указав в фигурных скобках до двоеточия номер аргумента (нумерация начинается с нуля):
#include <iostream> #include <format> int main() { int a{10}; int b{20}; int c{30}; std::cout << std::format("{2:} {1:} {0:}", a, b, c); // 30 20 10 }
В данном случае хотя в функции std:format
первым аргументом для вывода является число a
, однако в строке форматирования первым выводится
третий аргумент - {2:}
. И подобным образом можно указать вывод для других аргументов.
Явное указание индексов аргументов может быть полезным, когда нам надо вывести один и тот же аргумент несколько раз. Например, выведем одно и то же число в десятичной, двоичной и шестнадцатеричной форме:
#include <iostream> #include <format> int main() { int a{10}; int b{20}; int c{30}; std::cout << std::format("{0:} {0:b} {0:X}", 62); // 62 111110 3E }