Format String Syntax

The formatting functions in this library — most notably fmt::format and fmt::print — accept format strings written in the syntax described here.

A format string is plain text with embedded replacement fields delimited by the braces { and }. Characters outside of any replacement field are copied to the output unchanged. To emit a literal brace, double it: {{ yields a single { in the output, and }} yields a single }.

A replacement field is described by the grammar below.

replacement_field ::= "{" [arg_id] [":" (<a href="#format-spec"
  >format_spec</a> | <a href="#chrono-format-spec">chrono_format_spec</a>)] "}"
arg_id            ::= integer | identifier
integer           ::= digit+
digit             ::= "0"..."9"
identifier        ::= id_start id_continue*
id_start          ::= "a"..."z" | "A"..."Z" | "_"
id_continue       ::= id_start | digit

An arg_id selects which argument to format. It may be a non-negative integer (positional reference) or an identifier matching the name of an argument passed via fmt::arg (named reference). When arg_id is omitted, arguments are consumed in left-to-right order; this automatic indexing must be used uniformly throughout the format string — mixing automatic and explicit numeric ids is a compile-time error (or a format_error at runtime).

A format_spec, introduced by :, describes how the value should be rendered. Its grammar is type-dependent; the form used by the standard built-in types is documented in the next section.

For example:

fmt::format("hello, {}", "world");
// Result: "hello, world"

fmt::format("{1}, {0}!", "world", "hello");
// Result: "hello, world!"

fmt::format("{greeting}, {name}!",
            fmt::arg("greeting", "hi"), fmt::arg("name", "fmt"));
// Result: "hi, fmt!"

A width or precision inside a format_spec may itself be written as a nested replacement field — {} or {arg_id} — in which case it takes its value from an integer argument at runtime. Nested fields accept only an arg_id; they cannot themselves contain a format_spec.

Format Specification

The grammar below describes the format_spec shared by the built-in types — integers, floating-point values, characters, strings, booleans, and pointers — as well as by any user-defined type whose formatter reuses fmt's parser.

format_spec ::= [[fill]align][sign]["#"]["0"][width]["." precision]["L"][type]
fill        ::= <a character other than '{' or '}'>
align       ::= "<" | ">" | "^"
sign        ::= "+" | "-" | " "
width       ::= <a href="#replacement-field">integer</a> | "{" [<a
  href="#replacement-field">arg_id</a>] "}"
precision   ::= <a href="#replacement-field">integer</a> | "{" [<a
  href="#replacement-field">arg_id</a>] "}"
type        ::= "a" | "A" | "b" | "B" | "c" | "d" | "e" | "E" | "f" | "F" |
                "g" | "G" | "o" | "p" | "s" | "x" | "X" | "?"

Whether a particular option is meaningful depends on the value being formatted; options that do not apply to a value's type are diagnosed at compile time when possible and otherwise raise a format_error.

Fill and alignment

The align field selects where padding is placed when width makes the field wider than the value's natural rendering.

OptionEffect
<Left-align; pad on the right. Default for non-numeric types.
>Right-align; pad on the left. Default for numeric types.
^Center the value; if the padding cannot be split evenly, the extra padding character goes on the right.

The fill character is any single Unicode code point other than { or }, encoded the same way as the format string. It supplies the padding character in place of the default space. A fill character is recognized only when it is immediately followed by an align character — otherwise it would be indistinguishable from an option in another position — so to use a custom fill you must also specify an alignment.

Alignment has no observable effect when the value's natural rendering is already at least as wide as width; the value is never truncated to fit.

fmt::format("[{:<10}]", "42");   // Result: "[42        ]"
fmt::format("[{:>10}]", "42");   // Result: "[        42]"
fmt::format("[{:^10}]", "42");   // Result: "[    42    ]"
fmt::format("[{:*^10}]", "42");  // Result: "[****42****]"  - '*' as fill

Sign

The sign field controls how the sign of a numeric value is emitted. It applies to signed integer and floating-point types only.

OptionEffect
+Always emit a sign (+ for nonnegative values, - for negative).
-Emit - only for negative values. This is the default.
spaceEmit a leading space for nonnegative values and - for negative ones; useful for aligning columns of signed numbers.

The sign of -0.0 is preserved in floating-point output.

fmt::format("{:+d} {:+d}", 7, -7);  // Result: "+7 -7"
fmt::format("{: d} {: d}", 7, -7);  // Result: " 7 -7"

Alternate form (#)

The # flag selects an alternate form whose exact meaning depends on the presentation type:

  • For integers rendered in binary, octal, or hexadecimal, it prepends the appropriate base prefix (0b/0B, 0, or 0x/0X). The case of the prefix follows the case of the type specifier — 0x for x, 0X for X, and so on.
  • For floating-point values, it forces the decimal point to appear in the output even if no fractional digits would otherwise be emitted, and prevents the g/G presentation types from removing trailing zeros from the significand.

The # flag is not accepted by non-numeric types.

Zero padding (0)

A 0 placed immediately before width enables sign-aware zero padding for numeric types. Zeros are inserted between the sign (or base prefix, if any) and the most significant digit, so that a sign or 0x prefix stays adjacent to the digits rather than being separated by spaces. For example, {:+08d} applied to 120 produces +0000120.

Zero padding:

  • applies only to numeric types;
  • has no effect on inf or nan;
  • is ignored when an explicit align is also present.

Width

width is a non-negative decimal integer giving the minimum number of characters that the field should occupy. If the formatted value is shorter than width, it is padded according to align and fill; if it is longer, the value is written in full. width never causes the value to be truncated.

To supply width at runtime, write the field as {} to consume the next argument, or as {arg_id} to reference an integer argument by position or by name.

When formatting strings, “width” is measured in display columns using a Unicode-aware estimate (East Asian wide and fullwidth characters, plus common emoji ranges, count as two columns; everything else counts as one). This keeps fixed width values visually consistent in monospace renderings that combine Latin and CJK text.

fmt::format("[{:6}]", 42);
// Result: "[    42]"  - right-aligned by default
fmt::format("[{:6}]", "hi");
// Result: "[hi    ]"  - left-aligned by default
fmt::format("[{:{}}]", 42, 6);
// Result: "[    42]"  - width from an argument

Precision

precision is a non-negative decimal integer (introduced by .) whose meaning depends on the value being formatted. As with width, it may be supplied as a nested replacement field for runtime evaluation.

TypeMeaning of .precision
e, E, f, FDigits emitted after the decimal point.
g, GTotal number of significant digits.
a, ADigits after the decimal point in the hexadecimal significand. If omitted, just enough digits are emitted to round-trip the value exactly.
Strings (s, ?, or default)Upper bound on the number of code points copied from the value.

A precision is not accepted for integer, character, boolean, or pointer types. When a precision limits the number of characters taken from a C string, the string must still be null-terminated.

fmt::format("{:.2f}", 3.14159);        // Result: "3.14"
fmt::format("{:.3g}", 3.14159);        // Result: "3.14"
fmt::format("{:.4}", "hello, world");  // Result: "hell"
fmt::format("{:.{}f}", 3.14159, 4);
// Result: "3.1416"  - precision from an argument

Locale (L)

The L flag selects locale-sensitive formatting for numeric types. The formatter inspects the C++ locale supplied to the formatting function (or the global locale, if none was passed) and inserts the locale's digit grouping characters and — for floating-point values — its decimal point. The flag has no effect on non-numeric types.

auto loc = std::locale("en_US.UTF-8");
fmt::format(loc, "{:L}", 1234567890);     // Result: "1,234,567,890"
fmt::format(loc, "{:.2Lf}", 1234567.89);  // Result: "1,234,567.89"

Presentation type

The type field chooses the representation for the value. Specifiers are grouped below by the value categories they apply to.

Integers, booleans, and characters:

TypeEffect
bBase 2. The # flag adds a 0b prefix.
BBase 2. The # flag adds a 0B prefix.
cRender the integer as the character with that code point. Not allowed for bool.
dBase 10. The default for integer types.
oBase 8.
xBase 16, lower-case digits. The # flag adds a 0x prefix.
XBase 16, upper-case digits. The # flag adds a 0X prefix.
noneSame as d for integers, c for characters, and the textual form (true/false) for bool.
fmt::format("{:d} {:#x} {:#o} {:#b}", 42, 42, 42, 42);
// Result: "42 0x2a 052 0b101010"

fmt::format("{:#06x}", 0xfe);  // # adds the prefix, 06 zero-pads to width 6
// Result: "0x00fe"

Floating-point values:

TypeEffect
aHexadecimal-significand form (e.g. 0x1.8p+1). Lower-case digits and a lower-case p for the binary exponent. The 0x prefix is always emitted, matching printf's %a. The # flag forces a decimal point (e.g. 0x1.p+1).
ASame as a, but upper-case throughout.
eScientific notation with a lower-case e for the decimal exponent.
EScientific notation with an upper-case E.
fFixed-point notation.
FSame as f, but renders nan as NAN and inf as INF.
gGeneral form: scientific notation when the exponent would be less than −4 or not less than the precision, otherwise fixed-point; trailing zeros are removed from the fractional part unless # is set. A precision of 0 is interpreted as 1.
GSame as g, but uses E for the exponent and upper-case INF/NAN.
noneShortest round-trip representation: the formatted value, when parsed back into the same floating-point type, reproduces the input bit for bit.

Strings and characters:

TypeEffect
sPlain string output. Default for string types and for bool (which is rendered as true or false).
cCharacter output. Default for character types. Not allowed for bool.
?Debug output: the value is wrapped in single quotes (characters) or double quotes (strings), and non-printable, non-ASCII, and special characters are escaped using C-style escape sequences such as \n, \t, \", and \u{...}.
noneSame as s for strings and bool, and as c for characters.
fmt::format("{}",   "tab\there");  // Result contains a literal tab character.
fmt::format("{:?}", "tab\there");  // Result: "\"tab\\there\""

Pointers:

TypeEffect
pHexadecimal address prefixed by 0x. Default for pointer types.
noneSame as p.

A C string (char* or const char*) accepts both the string presentation types and p, so the same value can be formatted as either text or an address.

Chrono Format Specification

The format specification for chrono duration and time point types as well as std::tm has the following syntax:

chrono_format_spec ::= [[<a href="#format-spec">fill</a>]<a href="#format-spec"
  >align</a>][<a href="#format-spec">width</a>]["." <a href="#format-spec"
  >precision</a>][chrono_specs]
chrono_specs       ::= conversion_spec |
                       chrono_specs (conversion_spec | literal_char)
conversion_spec    ::= "%" [padding_modifier] [locale_modifier] chrono_type
literal_char       ::= <a character other than '{', '}' or '%'>
padding_modifier   ::= "-" | "_"
locale_modifier    ::= "E" | "O"
chrono_type        ::= "a" | "A" | "b" | "B" | "c" | "C" | "d" | "D" | "e" |
                       "F" | "g" | "G" | "h" | "H" | "I" | "j" | "m" | "M" |
                       "n" | "p" | "q" | "Q" | "r" | "R" | "S" | "t" | "T" |
                       "u" | "U" | "V" | "w" | "W" | "x" | "X" | "y" | "Y" |
                       "z" | "Z" | "%"

Literal chars are copied unchanged to the output. Precision is valid only for std::chrono::duration types with a floating-point representation type.

The available presentation types (chrono_type) are:

Specifiers that have a calendaric component such as 'd' (the day of month) are valid only for std::tm and time points but not durations.

The available padding modifiers (padding_modifier) are:

TypeMeaning
'_'Pad a numeric result with spaces.
'-'Do not pad a numeric result string.

These modifiers are only supported for the 'H', 'I', 'M', 'S', 'U', 'V', 'W', 'Y', 'd', 'j' and 'm' presentation types.

Example:

#include <fmt/chrono.h>

auto t = std::tm();
t.tm_year = 2010 - 1900;
t.tm_mon = 7;
t.tm_mday = 4;
t.tm_hour = 12;
t.tm_min = 15;
t.tm_sec = 58;
fmt::print("{:%Y-%m-%d %H:%M:%S}", t);
// Prints: 2010-08-04 12:15:58

Range Format Specification

The format specification for range types has the following syntax:

range_format_spec ::= [[fill]align][width]["n"][range_type][":" range_underlying_spec]

The fill, align and width options have the same meaning as in the standard format specification, except that : cannot be used as a fill character because it introduces the underlying element specification. They apply to the whole formatted range rather than to its elements.

The 'n' option formats the range without the opening and closing brackets.

The available presentation types for range_type are:

TypeMeaning
noneDefault format.
's'String format. The range is formatted as a string.
'?⁠s'Debug format. The range is formatted as an escaped string.

If range_type is 's' or '?s', the range element type must be a character type. The 'n' option and range_underlying_spec are mutually exclusive with 's' and '?s'.

The range_underlying_spec is parsed based on the formatter of the range's element type.

By default, a range of characters or strings is printed escaped and quoted. But if any range_underlying_spec is provided (even if it is empty), then the characters or strings are printed according to the provided specification.

Examples:

fmt::print("{}", std::vector{10, 20, 30});
// Output: [10, 20, 30]
fmt::print("{::#x}", std::vector{10, 20, 30});
// Output: [0xa, 0x14, 0x1e]
fmt::print("{}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: ['h', 'e', 'l', 'l', 'o']
fmt::print("{:n}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: 'h', 'e', 'l', 'l', 'o'
fmt::print("{:s}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: "hello"
fmt::print("{:?s}", std::vector{'h', 'e', 'l', 'l', 'o', '\n'});
// Output: "hello\n"
fmt::print("{::}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: [h, e, l, l, o]
fmt::print("{::d}", std::vector{'h', 'e', 'l', 'l', 'o'});
// Output: [104, 101, 108, 108, 111]
fmt::print("{:n:f}", std::array{std::numbers::pi, std::numbers::e});
// Output: 3.141593, 2.718282

A Combined Example

The example below ties together several elements introduced above — nested replacement fields, fill characters, and centering — to draw a fixed-width box around a message:

fmt::print(
    "┌{0:─^{2}}┐\n"
    "│{1: ^{2}}│\n"
    "└{0:─^{2}}┘\n", "", "Hello, world!", 20);

Output:

┌────────────────────┐
│   Hello, world!    │
└────────────────────┘