命名空间
变体
操作

标准格式规范 (C++20 起)

来自 cppreference.cn
< cpp‎ | 工具‎ | 格式化
 
 
 
 

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

格式规范的语法是

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

符号#0 选项仅在使用整数或浮点表示类型时有效。

目录

[编辑] 填充和对齐

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

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

对齐选项的含义如下

  • <: 通过在格式化参数后插入 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");
}

# 选项导致转换使用备用形式

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

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 is ignored because of '<'
}

[编辑] 宽度和精度

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

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

  • 对于浮点类型,此字段指定格式化精度。
  • 对于字符串类型,它为要复制到输出的字符串前缀的估计宽度(参见下文)提供上限。对于 Unicode 编码中的字符串,要复制到输出的文本是最长的前缀,由完整扩展字素簇组成,其估计宽度不大于精度。

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

float pi = 3.14f;
assert(std::format("{:10f}", pi)           == "  3.140000"); // width = 10
assert(std::format("{:{}f}", pi, 10)       == "  3.140000"); // width = 10
assert(std::format("{:.5f}", pi)           == "3.14000");    // precision = 5
assert(std::format("{:.{}f}", pi, 5)       == "3.14000");    // precision = 5
assert(std::format("{:10.5f}", pi)         == "   3.14000"); // width = 10, precision = 5
assert(std::format("{:{}.{}f}", pi, 10, 5) == "   3.14000"); // width = 10, precision = 5
 
auto b1 = std::format("{:{}f}", pi, 10.0); // throws: width is not of integral type
auto b2 = std::format("{:{}f}", pi, -10);  // throws: width is negative
auto b3 = std::format("{:.{}f}", pi, 5.0); // throws: precision is not of integral type

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

为了宽度计算的目的,字符串假定为实现定义的编码。宽度计算方法未指定,但对于 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::truenamestd::numpunct::falsename 获得。

[编辑] 类型

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

可用的字符串表示类型为

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

charwchar_tbool 之外的整型可用的整数表示类型为

  • b: 二进制格式。生成输出,如同通过调用 std::to_chars(first, last, value, 2)。基数前缀为 0b
  • B: 与 b 相同,只是基数前缀为 0B
  • c: 将字符 static_cast<CharT>(value) 复制到输出,其中 CharT 是格式字符串的字符类型。如果 value 不在 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 相同。

可用的 charwchar_t 表示类型为

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

可用的 bool 表示类型为

  • 无, s: 将文本表示(truefalse,或区域设置特定形式)复制到输出。
  • 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 分别格式化为 infnan。对于大写表示类型,无穷大和 NaN 分别格式化为 INFNAN

可用的指针表示类型(也用于 std::nullptr_t)为

  • 无, p: 如果定义了 std::uintptr_t,则生成输出,如同通过调用 std::to_chars(first, last, reinterpret_cast<std::uintptr_t>(value), 16),并在输出中添加前缀 0x;否则,输出是实现定义的。
  • P: 与 p 相同,只是数字大于 9 时使用大写字母,基数前缀为 0X
(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 标量值,其 Unicode 属性 General_Category 的值为 Separator (Z) 或 Other (C) 组中的值,或者
  • C 前面没有紧跟非转义字符,并且 C 对应于一个具有 Unicode 属性 Grapheme_Extend=Yes 的 Unicode 标量值,或者
  • 关联的字符编码不是 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");             // prints: ["h\tllo"]
    std::println("[{:?}]", "Спасибо, Виктор ♥!"); // prints: ["Спасибо, Виктор ♥!"]
    std::println("[{:?}] [{:?}]", '\'', '"');     // prints: ['\'', '"']
 
    // The following examples assume use of the UTF-8 encoding
    std::println("[{:?}]", std::string("\0 \n \t \x02 \x1b", 9));
                                             // prints: ["\u{0} \n \t \u{2} \u{1b}"]
    std::println("[{:?}]", "\xc3\x28");      // invalid UTF-8
                                             // prints: ["\x{c3}("]
    std::println("[{:?}]", "\u0301");        // prints: ["\u{301}"]
    std::println("[{:?}]", "\\\u0301");      // prints: ["\\\u{301}"]
    std::println("[{:?}]", "e\u0301\u0323"); // prints: ["ẹ́"]
}
(C++23 起)

[编辑] 注意

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

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

[编辑] 缺陷报告

下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。

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