命名空间
变体
操作

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

来自 cppreference.cn
< cpp‎ | utility‎ | format
 
 
 
 

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

格式规范的语法是

fill-and-align (可选) sign (可选) #(可选) 0(可选) width (可选) precision (可选) L(可选) type (可选)

sign、#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 字符和 align 选项都出现,则忽略 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 的值为 Fullwidth (F) 或 Wide (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:如果指定了 precision,则生成输出,就像调用 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 指示指数。
  • 无:如果指定了 precision,则生成输出,就像调用 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 起)

[编辑] 注释

在大多数情况下,语法类似于旧的 %-formatting,添加了 {} 并使用 : 代替 %。 例如,"%03.2f" 可以转换为 "{:03.2f}"

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

[编辑] 缺陷报告

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

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