Форматирование строк и функция format

Последнее обновление: 13.02.2023

Для более удобного форматирования строк начиная со стандарта 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

Параметр 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
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850