Namespaces
Variants

Standard format specification (since C++20)

From cppreference.net

对于基础类型和字符串类型,格式规范基于 Python中的格式规范

格式规范的语法如下:

填充与对齐  (可选) 符号  (可选) # (可选) 0 (可选) 宽度  (可选) 精度  (可选) L (可选) 类型  (可选)

当使用整数或浮点数表示类型时, sign # 0 选项才有效。

目录

填充与对齐

fill-and-align 是一个可选的 填充 字符(可以是除 { } 之外的任意字符),后接一个 对齐 选项 < > ^

如果未指定填充字符,则默认使用空格字符。对于 Unicode 编码中的格式规范,填充字符必须对应单个 Unicode 标量值。

align 选项的含义如下:

  • < :强制将格式化参数对齐到可用空间的起始位置,通过在格式化参数后插入 n 个填充字符实现。这是使用非整数非浮点数表示类型时的默认对齐方式。
  • > :强制将格式化参数对齐到可用空间的末尾,通过在格式化参数前插入 n 个填充字符实现。这是使用整数或浮点数表示类型时的默认对齐方式。
  • ^ :强制将格式化参数在可用空间内居中对齐,通过在格式化参数前插入
    n
    2
    个字符,并在其后插入
    n
    2
    个字符实现。

在每种情况下, n 表示最小字段宽度(由 width 指定)与格式化参数的 预估宽度 之差,若差值小于 0 则取 0。

#include <cassert>
#include <format>
int main()
{
    char c = 120;
    assert(std::format("{:6}", 42)    == "    42");
    assert(std::format("{:6}", 'x')   == "x     ");
    assert(std::format("{:*<6}", 'x') == "x*****");
    assert(std::format("{:*>6}", 'x') == "*****x");
    assert(std::format("{:*^6}", 'x') == "**x***");
    assert(std::format("{:6d}", c)    == "   120");
    assert(std::format("{:6}", true)  == "true  ");
}

符号、# 与 0

sign 选项可为下列值之一:

  • + :表示对非负数和负数都应使用符号。对于非负数,会在输出值前插入 + 号。
  • - :表示仅对负数使用符号(这是默认行为)。
  • 空格:表示对非负数使用前导空格,对负数使用减号。

负零被视为负数。

sign 选项适用于浮点数的无穷大和 NaN。

#include <cassert>
#include <format>
#include <limits>
int main()
{
    double inf = std::numeric_limits<double>::infinity();
    double nan = std::numeric_limits<double>::quiet_NaN();
    assert(std::format("{0:},{0:+},{0:-},{0: }", 1)   == "1,+1,1, 1");
    assert(std::format("{0:},{0:+},{0:-},{0: }", -1)  == "-1,-1,-1,-1");
    assert(std::format("{0:},{0:+},{0:-},{0: }", inf) == "inf,+inf,inf, inf");
    assert(std::format("{0:},{0:+},{0:-},{0: }", nan) == "nan,+nan,nan, nan");
}

# 选项将导致转换使用 替代形式

  • 对于整数类型,当使用二进制、八进制或十六进制表示形式时,备用形式会在符号字符(可能是空格)后插入前缀( 0b 0 0x )(如果存在符号字符),否则会在输出值前添加该前缀。
  • 对于浮点类型,备用形式会使有限值的转换结果始终包含小数点字符,即使其后没有数字。通常,这些转换的结果中仅当有数字跟随时才会出现小数点字符。此外,对于 g G 转换,不会从结果中移除尾随零。

0 选项会在字段宽度内用前导零填充字段(位于符号或进制指示符之后),但应用于无穷大或 NaN 时除外。如果同时出现 0 字符和 对齐 选项,则忽略 0 字符。

#include <cassert>
#include <format>
int main()
{
    char c = 120;
    assert(std::format("{:+06d}", c)   == "+00120");
    assert(std::format("{:#06x}", 0xa) == "0x000a");
    assert(std::format("{:<06}", -42)  == "-42   "); // 因 '<' 而忽略 0
}

宽度与精度

width 可以是一个正十进制数,或一个嵌套的替换字段( {} { n } )。如果存在,它指定最小字段宽度。

精度 是一个点( . )后跟一个非负十进制数或嵌套替换字段。该字段表示精度或最大字段大小。它只能用于浮点数和字符串类型。

  • 对于浮点数类型,此字段指定格式化精度。
  • 对于字符串类型,它提供了待复制到输出的字符串前缀估计宽度的上限(参见 下文 )。对于Unicode编码的字符串,将被复制到输出的文本是完整扩展字符簇的最长前缀,其估计宽度不超过精度值。

如果对 width precision 使用了嵌套替换字段,且对应参数不是 整数类型 (C++23 前) 标准有符号或无符号整数类型 (C++23 起) ,或为负值,将抛出类型为 std::format_error 的异常。

float pi = 3.14f;
assert(std::format("{:10f}", pi)           == "  3.140000"); // 宽度 = 10
assert(std::format("{:{}f}", pi, 10)       == "  3.140000"); // 宽度 = 10
assert(std::format("{:.5f}", pi)           == "3.14000");    // 精度 = 5
assert(std::format("{:.{}f}", pi, 5)       == "3.14000");    // 精度 = 5
assert(std::format("{:10.5f}", pi)         == "   3.14000"); // 宽度 = 10, 精度 = 5
assert(std::format("{:{}.{}f}", pi, 10, 5) == "   3.14000"); // 宽度 = 10, 精度 = 5
auto b1 = std::format("{:{}f}", pi, 10.0); // 抛出异常:宽度不是整数类型
auto b2 = std::format("{:{}f}", pi, -10);  // 抛出异常:宽度为负值
auto b3 = std::format("{:.{}f}", pi, 5.0); // 抛出异常:精度不是整数类型

字符串的宽度定义为在终端中显示它所需的预估列位置数。

出于宽度计算的目的,字符串被假定采用实现定义的编码。宽度计算的方法未作规定,但对于采用 Unicode 编码的字符串,实现应将其宽度估算为字符串中各 扩展字素簇 首码点的估算宽度之和。以下码点的估算宽度为 2,其余码点的估算宽度为 1:

  • 任何Unicode属性 East_Asian_Width 值为全角( F )或宽形( W )的码点
  • U+4DC0 - U+4DFF(易经六十四卦符号)
  • U+1F300 – U+1F5FF(杂项符号与象形文字)
  • U+1F900 – U+1F9FF(补充符号与象形文字)
#include <cassert>
#include <format>
int main()
{
    assert(std::format("{:.^5s}",   "🐱")    == ".🐱..");
    assert(std::format("{:.5s}",    "🐱🐱🐱") == "🐱🐱");
    assert(std::format("{:.<5.5s}", "🐱🐱🐱") == "🐱🐱.");
}

L (本地化特定格式化)

L 选项将使用区域设置特定的格式。此选项仅对算术类型有效。

  • 对于整数类型,本地化形式会根据上下文环境的区域设置插入适当的数字分组分隔符。
  • 对于浮点数类型,本地化形式会根据上下文环境的区域设置插入适当的数字分组和小数点分隔符。
  • 对于 bool 类型的文本表示,本地化形式会使用相应的字符串,如同通过 std::numpunct::truename std::numpunct::falsename 获取的那样。

类型

type 选项决定了数据应如何呈现。

可用的字符串表示类型有:

  • none, s : 将字符串复制到输出。
  • ? : 将转义字符串(参见 下文 )复制到输出。
(since C++23)

char wchar_t bool 之外的整数类型可用的整数表示类型为:

  • b :二进制格式。输出效果等同于调用 std:: to_chars ( first, last, value, 2 ) 。基数前缀为 0b
  • B :与 b 相同,但基数前缀为 0B
  • c :将字符 static_cast < CharT > ( value ) 复制到输出,其中 CharT 是格式字符串的字符类型。若数值不在 CharT 的可表示值范围内,则抛出 std::format_error
  • d :十进制格式。输出效果等同于调用 std:: to_chars ( first, last, value )
  • o :八进制格式。输出效果等同于调用 std:: to_chars ( first, last, value, 8 ) 。基数前缀在对应参数值非零时为 0 ,否则为空。
  • x :十六进制格式。输出效果等同于调用 std:: to_chars ( first, last, value, 16 ) 。基数前缀为 0x
  • X :与 x 相同,但对大于9的数字使用大写字母,且基数前缀为 0X
  • 无格式说明符:与 d 相同。

可用的 char wchar_t 表示类型包括:

  • none, c : 将字符复制到输出。
  • b , B , d , o , x , X : 使用整数表示类型,其值为 static_cast < unsigned char > ( value ) static_cast < std:: make_unsigned_t < wchar_t >> ( value )
  • ? : 将转义字符(参见 下文 )复制到输出。
(since C++23)

可用的 bool 表示类型包括:

  • 无, s :将文本表示形式( true false ,或特定区域设置的形式)复制到输出。
  • b B d o x X :使用整数表示类型,其值为 static_cast < unsigned char > ( value )

可用的浮点数表示类型包括:

  • a :若指定了 精度 ,则通过调用 std:: to_chars ( first, last, value, std :: chars_format :: hex , precision ) 生成输出,其中 precision 为指定精度;否则通过调用 std:: to_chars ( first, last, value, std :: chars_format :: hex ) 生成输出。
  • A :与 a 相同,但使用大写字母表示大于9的数字,并使用 P 表示指数。
  • e :通过调用 std:: to_chars ( first, last, value, std :: chars_format :: scientific , precision ) 生成输出,其中 precision 为指定精度,若未指定精度则默认为6。
  • E :与 e 相同,但使用 E 表示指数。
  • f F :通过调用 std:: to_chars ( first, last, value, std :: chars_format :: fixed , precision ) 生成输出,其中 precision 为指定精度,若未指定精度则默认为6。
  • g :通过调用 std:: to_chars ( first, last, value, std :: chars_format :: general , precision ) 生成输出,其中 precision 为指定精度,若未指定精度则默认为6。
  • G :与 g 相同,但使用 E 表示指数。
  • 无格式符:若指定了 精度 ,则通过调用 std:: to_chars ( first, last, value, std :: chars_format :: general , precision ) 生成输出,其中 precision 为指定精度;否则通过调用 std:: to_chars ( first, last, value ) 生成输出。

对于小写表示类型,无穷大和 NaN 分别格式化为 inf nan 。 对于大写表示类型,无穷大和 NaN 分别格式化为 INF NAN

std::format 格式说明符 std::chars_format 对应的 std::printf 说明符
a , A std::chars_format::hex a , A (但 std::format 不输出前导的 0x 0X
e , E std::chars_format::scientific e , E
f , F std::chars_format::fixed f , F
g , G std::chars_format::general g , G
若指定精度则为 std::chars_format::general ,否则为最短往返格式 若指定精度则为 g 。否则无对应说明符。

可用的指针表示类型(同样适用于 std::nullptr_t )包括:

  • P :与 p 相同,但对于大于9的数字使用大写字母,且基数前缀为 0X
(since C++26)


转义字符与字符串的格式化

字符或字符串可格式化为 转义 形式,使其更适用于调试或日志记录。

转义规则如下:

  • 对于每个编码字符 C 的合法码元序列:
  • C 为下表中字符,则使用对应的转义序列。
字符 转义序列 备注
水平制表符(ASCII编码中的字节0x09) \t
换行符(ASCII编码中的字节0x0a) \n
回车符(ASCII编码中的字节0x0d) \r
双引号(ASCII编码中的字节0x22) \" 仅当输出为双引号字符串时使用
单引号(ASCII编码中的字节0x27) \' 仅当输出为单引号字符串时使用
反斜杠(ASCII编码中的字节0x5c) \\
  • 否则,若 C 不是空格字符(ASCII编码中的字节0x20),且满足以下任一条件:
  • 关联字符编码为Unicode编码且
  • C 对应的Unicode标量值其 General_Category 属性属于 Separator Z )或 Other C )分组,或
  • C 之前无未转义字符,且 C 对应的Unicode标量值具有 Grapheme_Extend=Yes 属性,或
  • 关联字符编码非Unicode编码且 C 属于实现定义的分隔符或不可打印字符集合
则转义序列为 \u{ hex-digit-sequence } ,其中 hex-digit-sequence C 的最短小写十六进制表示。
  • 否则, C 按原样复制。
  • 作为移位序列的码元序列对输出及字符串后续解码的影响未指定。
  • 其他码元(即非法码元序列中的码元)均替换为 \x{ hex-digit-sequence } ,其中 hex-digit-sequence 为该码元的最短小写十六进制表示。

字符串的转义表示通过转义字符串中的码元序列(如上所述)并用双引号包裹结果来构造。

字符的转义表示通过转义该字符(如上所述)并用单引号包裹结果来构造。

Compiler Explorer演示

#include <print>
int main()
{
    std::println("[{:?}]", "h\tllo");             // 输出: ["h\tllo"]
    std::println("[{:?}]", "Спасибо, Виктор ♥!"); // 输出: ["Спасибо, Виктор ♥!"]
    std::println("[{:?}] [{:?}]", '\'', '"');     // 输出: ['\'', '"']
    // 以下示例假设使用UTF-8编码
    std::println("[{:?}]", std::string("\0 \n \t \x02 \x1b", 9));
                                             // 输出: ["\u{0} \n \t \u{2} \u{1b}"]
    std::println("[{:?}]", "\xc3\x28");      // 无效UTF-8
                                             // 输出: ["\x{c3}("]
    std::println("[{:?}]", "\u0301");        // 输出: ["\u{301}"]
    std::println("[{:?}]", "\\\u0301");      // 输出: ["\\\u{301}"]
    std::println("[{:?}]", "e\u0301\u0323"); // 输出: ["ẹ́"]
}
(C++23 起)

注释

在大多数情况下,新语法与旧的 % 格式化方式类似,增加了 {} 的使用,并用 : 替代 % 。例如, "%03.2f" 可转换为 "{:03.2f}"

功能测试 标准 功能
__cpp_lib_format_uchar 202311L (C++20)
(DR)
将代码单元格式化为无符号整数

缺陷报告

下列行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

缺陷报告 适用标准 发布时行为 正确行为
LWG 3721 C++20 标准格式规范中宽度字段
不允许使用零值
若通过替换字段指定
则允许使用零值
P2909R4 C++20 char wchar_t 可能被格式化为
超出范围的无符号整数值
在进行此类格式化前,代码单元
会先转换为对应的无符号类型