fmt/doc/syntax.md
2026-06-02 11:02:57 -07:00

26 KiB
Raw Blame History

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] [":" (format_spec | chrono_format_spec)] "}"
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       ::= integer | "{" [arg_id] "}"
precision   ::= integer | "{" [arg_id] "}"
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.

Option Effect
< 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.

Option Effect
+ Always emit a sign (+ for nonnegative values, - for negative).
- Emit - only for negative values. This is the default.
space Emit 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.

Type Meaning of .precision
e, E, f, F Digits emitted after the decimal point.
g, G Total number of significant digits.
a, A Digits 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:

Type Effect
b Base 2. The # flag adds a 0b prefix.
B Base 2. The # flag adds a 0B prefix.
c Render the integer as the character with that code point. Not allowed for bool.
d Base 10. The default for integer types.
o Base 8.
x Base 16, lower-case digits. The # flag adds a 0x prefix.
X Base 16, upper-case digits. The # flag adds a 0X prefix.
none Same 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:

Type Effect
a Hexadecimal-significand form (e.g. 1.8p+1). Lower-case digits and a lower-case p for the binary exponent. The # flag adds a 0x prefix.
A Same as a, but upper-case throughout.
e Scientific notation with a lower-case e for the decimal exponent.
E Scientific notation with an upper-case E.
f Fixed-point notation.
F Same as f, but renders nan as NAN and inf as INF.
g General 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.
G Same as g, but uses E for the exponent and upper-case INF/NAN.
none Shortest round-trip representation: the formatted value, when parsed back into the same floating-point type, reproduces the input bit for bit.

Strings and characters:

Type Effect
s Plain string output. Default for string types and for bool (which is rendered as true or false).
c Character 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{...}.
none Same 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:

Type Effect
p Hexadecimal address prefixed by 0x. Default for pointer types.
none Same 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 ::= [[fill]align][width]["." precision][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   ::= "-" | "_"  | "0"
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:

Type Meaning
'a' The abbreviated weekday name, e.g. "Sat". If the value does not contain a valid weekday, an exception of type format_error is thrown.
'A' The full weekday name, e.g. "Saturday". If the value does not contain a valid weekday, an exception of type format_error is thrown.
'b' The abbreviated month name, e.g. "Nov". If the value does not contain a valid month, an exception of type format_error is thrown.
'B' The full month name, e.g. "November". If the value does not contain a valid month, an exception of type format_error is thrown.
'c' The date and time representation, e.g. "Sat Nov 12 22:04:00 1955". The modified command %Ec produces the locale's alternate date and time representation.
'C' The year divided by 100 using floored division, e.g. "19". If the result is a single decimal digit, it is prefixed with 0. The modified command %EC produces the locale's alternative representation of the century.
'd' The day of month as a decimal number. If the result is a single decimal digit, it is prefixed with 0. The modified command %Od produces the locale's alternative representation.
'D' Equivalent to %m/%d/%y, e.g. "11/12/55".
'e' The day of month as a decimal number. If the result is a single decimal digit, it is prefixed with a space. The modified command %Oe produces the locale's alternative representation.
'F' Equivalent to %Y-%m-%d, e.g. "1955-11-12".
'g' The last two decimal digits of the ISO week-based year. If the result is a single digit it is prefixed by 0.
'G' The ISO week-based year as a decimal number. If the result is less than four digits it is left-padded with 0 to four digits.
'h' Equivalent to %b, e.g. "Nov".
'H' The hour (24-hour clock) as a decimal number. If the result is a single digit, it is prefixed with 0. The modified command %OH produces the locale's alternative representation.
'I' The hour (12-hour clock) as a decimal number. If the result is a single digit, it is prefixed with 0. The modified command %OI produces the locale's alternative representation.
'j' If the type being formatted is a specialization of duration, the decimal number of days without padding. Otherwise, the day of the year as a decimal number. Jan 1 is 001. If the result is less than three digits, it is left-padded with 0 to three digits.
'm' The month as a decimal number. Jan is 01. If the result is a single digit, it is prefixed with 0. The modified command %Om produces the locale's alternative representation.
'M' The minute as a decimal number. If the result is a single digit, it is prefixed with 0. The modified command %OM produces the locale's alternative representation.
'n' A new-line character.
'p' The AM/PM designations associated with a 12-hour clock.
'q' The duration's unit suffix.
'Q' The duration's numeric value (as if extracted via .count()).
'r' The 12-hour clock time, e.g. "10:04:00 PM".
'R' Equivalent to %H:%M, e.g. "22:04".
'S' Seconds as a decimal number. If the number of seconds is less than 10, the result is prefixed with 0. If the precision of the input cannot be exactly represented with seconds, then the format is a decimal floating-point number with a fixed format and a precision matching that of the precision of the input (or to a microseconds precision if the conversion to floating-point decimal seconds cannot be made within 18 fractional digits). The modified command %OS produces the locale's alternative representation.
't' A horizontal-tab character.
'T' Equivalent to %H:%M:%S.
'u' The ISO weekday as a decimal number (1-7), where Monday is 1. The modified command %Ou produces the locale's alternative representation.
'U' The week number of the year as a decimal number. The first Sunday of the year is the first day of week 01. Days of the same year prior to that are in week 00. If the result is a single digit, it is prefixed with 0. The modified command %OU produces the locale's alternative representation.
'V' The ISO week-based week number as a decimal number. If the result is a single digit, it is prefixed with 0. The modified command %OV produces the locale's alternative representation.
'w' The weekday as a decimal number (0-6), where Sunday is 0. The modified command %Ow produces the locale's alternative representation.
'W' The week number of the year as a decimal number. The first Monday of the year is the first day of week 01. Days of the same year prior to that are in week 00. If the result is a single digit, it is prefixed with 0. The modified command %OW produces the locale's alternative representation.
'x' The date representation, e.g. "11/12/55". The modified command %Ex produces the locale's alternate date representation.
'X' The time representation, e.g. "10:04:00". The modified command %EX produces the locale's alternate time representation.
'y' The last two decimal digits of the year. If the result is a single digit it is prefixed by 0. The modified command %Oy produces the locale's alternative representation. The modified command %Ey produces the locale's alternative representation of offset from %EC (year only).
'Y' The year as a decimal number. If the result is less than four digits it is left-padded with 0 to four digits. The modified command %EY produces the locale's alternative full year representation.
'z' The offset from UTC in the ISO 8601:2004 format. For example -0430 refers to 4 hours 30 minutes behind UTC. If the offset is zero, +0000 is used. The modified commands %Ez and %Oz insert a : between the hours and minutes: -04:30. If the offset information is not available, an exception of type format_error is thrown.
'Z' The time zone abbreviation. If the time zone abbreviation is not available, an exception of type format_error is thrown.
'%' A % character.

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:

Type Meaning
'_' Pad a numeric result with spaces.
'-' Do not pad a numeric result string.
'0' Pad a numeric result string with zeros.

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 ::= ["n"][range_type][":" range_underlying_spec]

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

The available presentation types for range_type are:

Type Meaning
none Default 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!    │
└────────────────────┘