The Formatting Library in C++20: The Format String
I introduced “The Formatting Library in C++20” in my last post. Today, I will dive deeper into the format specification of the format string.
Let me start with a short recap of the format string.
- Syntax:
std::format(FormatString, Args)
The format string FormatString
consists of
- Ordinary characters (except { and })
- Escape sequences {{ and }} that are replaced by { and }
- Replacement fields
A replacement field has the format { }.
Argument ID
You can use an argument id and a colon inside the replacement field followed by a format specification. Both components are optional. The argument id allows you to specify the index of the arguments in Args
. The ids start with 0. When you don’t provide the argument id, the fields are filled in the same order as the arguments are given. Either all replacement fields have to use an argument id or none.
Thanks to the argument id, you can reorder or address particular arguments.
// formatArgumentID.cpp #include <format> #include <iostream> #include <string> int main() { std::cout << '\n'; std::cout << std::format("{} {}: {}!\n", "Hello", "World", 2020); // (1) std::cout << std::format("{1} {0}: {2}!\n", "World", "Hello", 2020); // (2) std::cout << std::format("{0} {0} {1}: {2}!\n", "Hello", "World", 2020); // (3) std::cout << std::format("{0}: {2}!\n", "Hello", "World", 2020); // (4) std::cout << '\n'; }
Line (1) displays the argument in the given order. On the contrary, line (2) reorders the first and second argument, line (3) shows the first argument twice, and line (4) ignores the second argument.
For completeness, here is the output of the program:
Applying the argument id with the format specification makes formatting text in C++20 very powerful.
Format Specification
I won’t present the format specification for basic types, string types, or chrono types. For basic types and std::string
, read the full details here: standard format specification. Accordingly, you can find the details of chrono types here: chrono format specification.
Modernes C++ Mentoring
Do you want to stay informed: Subscribe.
Instead, I present a pragmatic description of the format string for basic types, string types, and chrono types.
fill_align(opt) sign(opt) #(opt) 0(opt) width(opt) precision(opt) L(opt) type(opt)
All parts are optional (opt). The following few sections present the features of this format specification.
Fill Character and Alignment
The fill character is optional (any character except {
or }
) and followed by an alignment specification. To use the fill character, you must specify the alignment.
- Fill character: by default, space is used
- Alignment:
<
: left (default for non-numbers)- >: right (default for numbers)
- ^: center
// formatFillAlign.cpp #include <format> #include <iostream> int main() { std::cout << '\n'; int num = 2020; std::cout << std::format("{:6}", num) << '\n'; std::cout << std::format("{:6}", 'x') << '\n'; std::cout << std::format("{:*<6}", 'x') << '\n'; std::cout << std::format("{:*>6}", 'x') << '\n'; std::cout << std::format("{:*^6}", 'x') << '\n'; std::cout << std::format("{:6d}", num) << '\n'; std::cout << std::format("{:6}", true) << '\n'; std::cout << '\n'; }
The default alignment depends on the used types. In contrast to the iostream operator, boolean values are displayed by default as true
or false
.
Sign, #
and 0
The sign, #
, and 0
character is only valid when an integer or floating-point type is used.
The sign can have the following values:
+
: sign is used for zero and positive numbers- -: sign is only used for negative numbers (default)
- space: leading space is used for non-negative numbers and a minus sign for negative numbers
// formatSign.cpp #include <format> #include <iostream> int main() { std::cout << '\n'; std::cout << std::format("{0:},{0:+},{0:-},{0: }", 0) << '\n'; std::cout << std::format("{0:},{0:+},{0:-},{0: }", -0) << '\n'; std::cout << std::format("{0:},{0:+},{0:-},{0: }", 1) << '\n'; std::cout << std::format("{0:},{0:+},{0:-},{0: }", -1) << '\n'; std::cout << '\n'; }
The #
causes the alternative form:
- For integer types, the prefix
0b
,0
, or0x
is used for binary, octal, or hexadecimal presented types - For floating-point types, a decimal point is always used
0
: pads with leading zeros
// formatAlternate.cpp #include <format> #include <iostream> int main() { std::cout << '\n'; std::cout << std::format("{:#015}", 0x78) << '\n'; std::cout << std::format("{:#015b}", 0x78) << '\n'; std::cout << std::format("{:#015x}", 0x78) << '\n'; std::cout << '\n'; std::cout << std::format("{:g}", 120.0) << '\n'; std::cout << std::format("{:#g}", 120.0) << '\n'; std::cout << '\n'; }
What’s Next?
The format specifier allows you to specify the width, precision, and the type of the value. I will write about it in my next post.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Stephen Kelley, Kyle Dean, Tusar Palauri, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, Marco Parri Empoli, Philipp Lenk, Charles-Jianye Chen, Keith Jeffery, Matt Godbolt, and Honey Sukesan.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
My special thanks to Embarcadero | |
My special thanks to PVS-Studio | |
My special thanks to Tipi.build | |
My special thanks to Take Up Code | |
My special thanks to SHAVEDYAKS |
Modernes C++ GmbH
Modernes C++ Mentoring (English)
Rainer Grimm
Yalovastraße 20
72108 Rottenburg
Mail: schulung@ModernesCpp.de
Mentoring: www.ModernesCpp.org
Modernes C++ Mentoring,