std::formatter
在标头 <format> 定义
|
||
template< class T, class CharT = char > struct formatter; |
(C++20 起) | |
std::formatter
的被启用特化对给定类型定义格式化规则。被启用特化必须满足格式化器 (Formatter) 要求。尤其是它们定义可调用实体 parse
与 format
。
对每组未对其启用特化 std::formatter<T, CharT>
的类型 T
与 CharT
,该特化是完整类型且被禁用。
被禁用特化不满足格式化器 (Formatter) 要求,且下列值都是 false:
- std::is_default_constructible_v
- std::is_copy_constructible_v
- std::is_move_constructible_v
- std::is_copy_assignable_v
- std::is_move_assignable_v
基本的标准特化
以下列表中,CharT
是 char 或 wchar_t,ArithmeticT
是任何无 cv 限定的算术类型,除了 char、wchar_t、char8_t、char16_t 或 char32_t。
字符格式化器 |
||
template<> struct formatter<char, char>; |
(1) | |
template<> struct formatter<char, wchar_t>; |
(2) | |
template<> struct formatter<wchar_t, wchar_t>; |
(3) | |
字符串格式化器 |
||
template<> struct formatter<CharT*, CharT>; |
(4) | |
template<> struct formatter<const CharT*, CharT>; |
(5) | |
template< std::size_t N > struct formatter<CharT[N], CharT>; |
(6) | |
template< std::size_t N > struct formatter<const CharT[N], CharT>; |
(7) | (C++23 前) |
template< class Traits, class Alloc > struct formatter<std::basic_string<CharT, Traits, Alloc>, CharT>; |
(8) | |
template< class Traits > struct formatter<std::basic_string_view<CharT, Traits>, CharT>; |
(9) | |
算术格式化器 |
||
template<> struct formatter<ArithmeticT, CharT>; |
(10) | |
指针格式化器 |
||
template<> struct formatter<std::nullptr_t, CharT>; |
(11) | |
template<> struct formatter<void*, CharT>; |
(12) | |
template<> struct formatter<const void*, CharT>; |
(13) | |
对其他指针和指向成员指针的格式化器被禁用。
如 std::formatter<wchar_t, char> 和 std::formatter<const char*, wchar_t> 这些会要求编码转换的特化被禁用。
每个对字符串或字符类型的 |
(C++23 起) |
标准格式说明
对于基本类型和字符串类型,格式说明基于 Python 中的格式说明。
格式说明的语法是:
填充与对齐 (可选) 符号 (可选) # (可选) 0 (可选) 宽度 (可选) 精度 (可选) L (可选) 类型 (可选)
|
|||||||||
符号、#
及 0
选项只有在使用整数或浮点显示类型时合法。
填充与对齐
填充与对齐 是一个可选的填充字符(可以是任何 {
和 }
以外的的字符),后随对齐 选项 <
、>
、^
之一。对齐 选项的意义如下:
-
<
:强制格式化的实参对齐到可用空间开头,通过在格式化的实参的后面插入 n 个字符。这是使用非整数非浮点显示类型时的默认选项。 -
>
:强制格式化的实参对齐到可用空间末尾,通过在格式化的实参的前面插入 n 个字符。这是使用整数或浮点显示类型时的默认选项。 -
^
:强制格式化的实参在可用空间中央,通过在格式化的实参的前面插入 ⌊
⌋ 个字符,后面插入 ⌈n 2
⌉ 个字符。n 2
在所有情况下,n 是最小域宽和格式化的实参的预计宽度的差,或者在差小于 0 时是 0。
char c = 120; auto s0 = std::format("{:6}", 42); // s0 的值是 " 42" auto s1 = std::format("{:6}", 'x'); // s1 的值是 "x " auto s2 = std::format("{:*<6}", 'x'); // s2 的值是 "x*****" auto s3 = std::format("{:*>6}", 'x'); // s3 的值是 "*****x" auto s4 = std::format("{:*^6}", 'x'); // s4 的值是 "**x***" auto s5 = std::format("{:6d}", c); // s5 的值是 " 120" auto s6 = std::format("{:6}", true); // s6 的值是 "true "
符号、# 与 0
符号 选项可以是下列之一:
-
+
:指示符号应该一同用于非负数和负数。在非负数的输出值前插入+
号。 -
-
:指示符号应该仅用于负数(这是默认行为)。 - 空格:指示应对非负数使用一个前导空格,而对负数使用负号。
负零被当作负数。
符号 选项应用于浮点无穷大和 NaN。
double inf = std::numeric_limits<double>::infinity(); double nan = std::numeric_limits<double>::quiet_NaN(); auto s0 = std::format("{0:},{0:+},{0:-},{0: }", 1); // s0 的值是 "1,+1,1, 1" auto s1 = std::format("{0:},{0:+},{0:-},{0: }", -1); // s1 的值是 "-1,-1,-1,-1" auto s2 = std::format("{0:},{0:+},{0:-},{0: }", inf); // s2 的值是 "inf,+inf,inf, inf" auto s3 = std::format("{0:},{0:+},{0:-},{0: }", nan); // s3 的值是 "nan,+nan,nan, nan"
#
选项导致将替用形式 用于转换。
- 对于整数类型,使用二进制、八进制或十六进制显示类型时,替用形式会在有符号字符(可以是空格)时将前缀(
0b
、0
或0x
)到输出值中符号字符后,否则将前缀插入到输出值前。 - 对于浮点类型,替用形式导致有限值的转换结果始终含有小数点字符,即使它后面没有数位。正常情况下,小数点字符只有在它后面有数位时才会在转换结果出现。另外,对于
g
与G
转换,不会从结果移除尾随的零。
0
选项以前导零填充域(后随任何符号或底)到域宽,除了应用到无穷大或 NaN 时。如果 0
字符与对齐 选项一同出现,那么忽略 0
字符。
char c = 120; auto s1 = std::format("{:+06d}", c); // s1 的值是 "+00120" auto s2 = std::format("{:#06x}", 0xa); // s2 的值是 "0x000a" auto s3 = std::format("{:<06}", -42); // s3 的值是 "-42 "(因 < 对齐忽略 0)
宽度与精度
宽度 是一个正十进制数或嵌套的替换域({}
或 {
n }
)。它存在的情况下会指定最小域宽。
精度 是点(.
)后随非负十进制数或嵌套的替换域。此域指示精度或最大域大小。它只能用于浮点与字符串类型。对于浮点类型,此域指定格式化精度。对于字符串类型,它提供要复制到输出的字符串前缀的估计宽度(见后述)的上界。对于以 Unicode 编码的字符串,复制到输出的文本是整个扩展字素集群的,使得估计宽度不大于精度的最长前缀。
如果宽度 或精度 中使用嵌套的替换域,而对应的参数不是整数类型 (C++23 前)标准有符号或无符号整数类型 (C++23 起),为负,或对于宽度 为零,那么就会抛出 std::format_error 类型异常。
float pi = 3.14f; auto s1 = std::format("{:10f}", pi); // s1 = " 3.140000" (宽度=10) auto s2 = std::format("{:{}f}", pi, 10); // s2 = " 3.140000" (宽度=10) auto s3 = std::format("{:.5f}", pi); // s3 = "3.14000" (精度=5) auto s4 = std::format("{:.{}f}", pi, 5); // s4 = "3.14000" (精度=5) auto s5 = std::format("{:10.5f}", pi); // s5 = " 3.14000" // (宽度=10,精度=5) auto s6 = std::format("{:{}.{}f}", pi, 10, 5); // s6 = " 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 的字符串,实现应该估计字符串的宽度为它的扩展字素集群中首个码位的估计宽度之和。如果 Unicode 码位在下列范围内,那么估计宽度是 2,否则是 1:
- 任意 Unicode 属性 East_Asian_Width 具有值 Fullwidth (F) 或 Wide (W) 的代码单元
- U+4DC0 – U+4DFF(易经六十四卦像)
- U+1F300 – U+1F5FF(其他符号和象形文字)
- U+1F900 – U+1F9FF(补充符号和象形文字)
auto s1 = std::format("{:.^5s}", "🐱"); // s1 = ".🐱.." auto s2 = std::format("{:.5s}", "🐱🐱🐱"); // s2 = "🐱🐱" auto s3 = std::format("{:.<5.5s}", "🐱🐱🐱"); // s3 = "🐱🐱."
L(本地环境特定的格式化)
L
选项导致使用本地环境特定的形式。此选项仅对算术类型合法。
- 对于整数类型,本地环境特定形式会按照上下文的本地环境插入适合的数位组分隔字符。
- 对于浮点类型,本地环境特定形式会按照上下文的本地环境插入适合的数位组和底分隔字符。
- 对于 bool 的文本表示,本地环境特定形式使用如同通过 std::numpunct::truename 或 std::numpunct::falsename 获得的字符串。
类型
类型 选项确定应该如何显示数据。
可用的字符串显示类型有:
- 无、
s
:复制字符串到输出。
可用的 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 表示类型有:
- 无、
c
:复制字符到输出。 -
b
、B
、d
、o
、x
、X
:使用整数表示类型。
可用的 bool 表示类型有:
- 无、
s
:复制文本表示(true
或false
或本地环境特定形式)到输出。 -
b
、B
、c
、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::nullptr_t)有:
- 无、
p
:如果定义了 std::uintptr_t,那么如同通过调用 std::to_chars(first, last, reinterpret_cast<std::uintptr_t>(value), 16) 产生输出,并添加前缀0x
到输出;否则输出由实现定义。
|
(C++26 起) |
格式化转义字符和字符串 (C++23 起)
字符或字符串可以在格式化时进行转义 ,适合用于调试或记录日志。
转义通过以下方式进行:
- 对于每个良构的编码了字符 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 标量具有的 Unicode 属性
Grapheme_Extend=Yes
,要么
- C 对应的 Unicode 标量具有的 Unicode 属性
- 关联的字符编码不是 Unicode 编码,并且 C 是由实现定义的不可打印字符集合中的字符,
- 那么转义序列是
\u{
十六进制数位转义序列 }
,其中十六进制数位转义序列 是 C 使用小写十六进制数位的最短十六进制表示。
- 否则,C 按原样复制。
- 未指定代码单元序列是移位序列时对输出和后续对字符串的解码造成的影响。
- 每个其他代码单元(即在非良构代码单元序列中的代码单元)都会替换成
\x{
十六进制数位转义序列 }
,其中十六进制数位转义序列 是代码单元使用小写十六进制数位的最短十六进制表示。
字符串的转义字符串表示会通过以上述方式对字符串中的代码单元序列进行转义来构造,并且用双引号包围结果。
字符的转义表示会通过以上述方式对它进行转义来构造,并且用单引号包围结果。
auto s1 = std::format("[{:?}]", "h\tllo"); // s1 的值:["h\tllo"] auto s2 = std::format("[{:?}]", "Спасибо, Виктор ♥!"); // s2 的值: // ["Спасибо, Виктор ♥!"] auto s3 = std::format("[{:?}] [{:?}]", '\'', '"'); // s3 的值:['\'', '"'] // 以下样例假定采用了 UTF-8 编码 auto s4 = std::format("[{:?}]", std::string("\0 \n \t \x02 \x1b", 9)); // s4 的值: // [\u{0} \n \t \u{2} \u{1b}] auto s5 = std::format("[{:?}]", "\xc3\x28"); // 非法 UTF-8 // s5 的值:["\x{c3}("] auto s6 = std::format("[{:?}]", "\u0301"); // s6 的值:["\u{301}"] auto s7 = std::format("[{:?}]", "\\\u0301"); // s7 的值:["\\\u{301}"] auto s8 = std::format("[{:?}]", "e\u0301\u0323"); // s8 的值:["ẹ́"]
库类型的标准特化
std::formatter 的特化,按提供的格式格式化 duration (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 sys_time (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 utc_time (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 tai_time (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 gps_time (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 file_time (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 local_time (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 day (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 month (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 year (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 weekday (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 weekday_indexed (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 weekday_last (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 month_day (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 month_day_last (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 month_weekday (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 month_weekday_last (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 year_month (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 year_month_day (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 year_month_day_last (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 year_month_weekday (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 year_month_weekday_last (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 hh_mm_ss (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 sys_info (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 local_info (类模板特化) | |
std::formatter 的特化,按照提供的格式格式化 zoned_time (类模板特化) |
示例
#include <format> #include <iostream> // 类型 T 的包装 template<class T> struct Box { T value; }; // 能用被包装值的格式说明格式化包装 Box<T> template<class T, class CharT> struct std::formatter<Box<T>, CharT> : std::formatter<T, CharT> { // 从基类继承 parse() // 通过以被包装值调用基类实现定义 format() template<class FormatContext> auto format(Box<T> t, FormatContext& fc) const { return std::formatter<T, CharT>::format(t.value, fc); } }; int main() { Box<int> v = {42}; std::cout << std::format("{:#x}", v); }
输出:
0x2a
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
LWG 3721 | C++20 | 标准格式化说明中不允许指定域宽为零 | 可以通过替换域指定 |
参阅
(C++20)(C++20)(C++20) |
格式化状态,包括所有格式化参数和输出迭代器 (类模板) |