使用string和string_view(三)——字符串格式化

1. std::format

  直到C++20之前,字符串的格式化一般是通过printf()之类的C风格函数或是C++的I/O流完成的:
  1. C风格函数:不推荐,因为它们不是类型安全的,并且无法扩展支持自定义类型;因为字符串和参数是分开的,所以可读性高,且容易翻译成不同语言。例如:
  printf("x has value %d and y has value %d.\n", x, y);
  2. C++ I/O流:推荐(C++20之前),因为类型安全且可扩展;因为字符串和参数交织在一起,所以可读性差,且难以翻译成不同语言。例如:
  std::cout << "x has value" << x << " and y has value " << y << std::endl;
  C++20引入了std::format(),用来格式化字符串,它定义在<format>中。它基本结合了C风格函数和C++的I/O流的所有优点,是一种类型安全且可扩展的机制。
  format()的第一个参数是待格式化的字符串,后续参数是用于填充待格式化字符串中占位符的值。到目前为止,使用format()时的占位符一般都是一对花括号:{}。在这些花括号内可以是格式为[index][:specifier]的字符串。可以省略所有占位符中的index,也可以为所有占位符指定从零开始的索引,以指明应用于此占位符的第二个和后续参数。如果省略index,则format()的第二个和后续参数传递的值将按给定顺序应用于所有占位符。specifier是一种格式化说明符,用于更改值在输出中格式化的方式,将在下一节详细说明。如果需要输出{or}字符,则需要将其转义为{{or}}。
  以下对format()的调用省略了占位符中的显式索引:
  auto s1 { std::format("Read {} bytes from {}\n", n, "file1.txt") };
  可以按以下方式手动指定索引:
  auto s1 { std::format("Read {0} bytes from {1}\n", n, "file1.txt") };
  不允许混合使用手动和自动索引。下例使用了一个无效的字符串格式化:
  auto s2 { std::format("Read {0} bytes from {}\n", n, "file1.txt") };
  可以更改输出字符串中格式化值的顺序,而不需要更改传递到format()的实参的实际顺序。如果要在软件中翻译字符串,这是一个有用的功能。某些语言在句子中不同的顺序。例如,前面的格式化字符串可以翻译成中文,如下所示。在中文中,句子中占位符的顺序是颠倒的,但是由于格式化字符串中的占位符是有索引的,format()函数的参数顺序保持不变(L前缀的用法后面介绍)。
  auto s3 { std::format(L"从{1}中读取{0}个字节\n", n, L"file1.txt") }:

2. 格式说明符

  格式说明符用于控制值在输出中的格式,前缀为冒号。格式说明符的一般形式如下所示(L与地区特定格式有关,但是在我国好像没啥变化)。
  [[fill]align][sign][#][0][width][.precision][L][type]
  方括号里的所有说明符都是可选的。

2.1 width

  width指定待格式化的值所占字段的最小宽度,例如5。width也可以是另一组花括号,称为动态宽度。如果在花括号中指定了索引,例如{3},则动态宽度的width取自给定索引对应的format()的实参。如果未指定索引,例如{},则width取自format()的实参列表中的下一个参数。示例如下:

	int i{ 42 };
	std::cout << std::format("|{:5}|\n", i);    // |   42|
	std::cout << std::format("|{:{}}|\n", i, 8);// |      42|

2.2 [fill]align

  [fill]align说明使用哪个字符作为填充字符,然后是值在其字段中的对齐方式。

对齐符号含义
<左对齐(非整数和非浮点数的默认对齐方式)
>右对齐(整数和浮点数的默认对齐方式)
^居中对齐,在待格式化字符前插入 ⌊ 宽度 − 待格式化字符串长度 2 ⌋ \lfloor\frac{宽度-待格式化字符串长度}{2}\rfloor 2宽度待格式化字符串长度个字符,在待格式化字符串之后插入 ⌈ 宽度 − 待格式化字符串长度 2 ⌉ \lceil\frac{宽度-待格式化字符串长度}{2}\rceil 2宽度待格式化字符串长度

  fill字符会被插入输出中,以确保输出中的字段达到说明符的[width]指定的最小宽度。如果未指定[width],则[fill]align无效。示例如下:

int i { 42 };
std::cout << std::format("|{:7}|\n", i);  // |     42|
std::cout << std::format("|{:<7}|\n", i); // |42     |
std::cout << std::format("|{:_>7}|\n", i);// |_____42|
std::cout << std::format("|{:_^7}|\n", i);// |__42___|

2.3 sign

sign符号含义
-只显示负数的符号(默认方式)
+显示正数和负数的符号
空格对负数使用负号,对于正数使用空格

  示例如下:

int i { 42 };
std::cout << std::format("|{:<5}|\n", i);  // |42   |
std::cout << std::format("|{:<+5}|\n", i); // |+42  |
std::cout << std::format("|{:< 5}|\n", i); // | 42  |
std::cout << std::format("|{:< 5}|\n", -i);// |-42  |

2.4

  #启用所谓的备用格式规则。如果为整型启用,并且还指定了十六进制、二进制或八进制数字格式,则备用格式会在格式化数字前面插入0x、0X、0b、0B或0。如果为浮点类型启用,则备用格式将始终输出十进制分隔符,即使后面没有数字。
  具体示例见下面。

2.5 type

  type指定了给定值要被格式化的类型:

类型符号含义
整型
b
B
d(整型默认类型)
o
x
X
二进制
二进制,当指定#时,使用0B而不是0b
十进制
八进制
小写字母abcde的十六进制
大写字母ABCDE的十六进制,当指定#时,使用0X而不是0x
浮点型
e,E
f,F
g(浮点型默认类型) ,G
a,A
以小写e或大写E表示指数的科学表示法,按照给定精度或6(如果未指定精度)格式化
固定表示法,按照给定精度或6(如果未指定精度)格式化
以小写e或大写E表示指数的通用表示法,按照给定精度或6(如果未指定精度)格式化
带有小写字母(a)或大写字母(A)的十六进制表示法
布尔型
s(布尔型默认类型)
b
B
c
d
o
x
X
以文本形式输出true或false






以整型输出1或0
字符型
c(字符型默认类型)
b
B
d
o
x
X
输出字符副本





整数表示
字符串s(字符串默认类型)输出字符串副本
指针p(指针默认类型)0x为前缀的十六进制表示法

  整型示例如下:

int i { 42 };
std::cout << std::format("|{:10d}|\n", i); // |        42|
std::cout << std::format("|{:10b}|\n", i); // |    101010|
std::cout << std::format("|{:#10b}|\n", i);// |  0b101010|
std::cout << std::format("|{:10o}|\n", i); // |        52|
std::cout << std::format("|{:#10o}|\n", i);// |       052|
std::cout << std::format("|{:10x}|\n", i); // |        2a|
std::cout << std::format("|{:#10X}|\n", i);// |      0X2A|

  布尔型示例如下:

bool t { true };
bool f { false };
std::cout << std::format("|{:10s}|\n", t); // |      true|
std::cout << std::format("|{:10b}|\n", f); // |			0|
std::cout << std::format("|{:10B}|\n", t); // |         1|
std::cout << std::format("|{:10d}|\n", f); // |         0|
std::cout << std::format("|{:10o}|\n", t); // |         1|
std::cout << std::format("|{:10x}|\n", f); // |         0|
std::cout << std::format("|{:10X}|\n", t); // |         1|

  字符型示例如下:

char a { 'c' };
std::cout << std::format("|{:10c}|\n", a);  // |c         |
std::cout << std::format("|{:#10b}|\n", a); // | 0b1100011|
std::cout << std::format("|{:#10B}|\n", a); // | 0B1100011|
std::cout << std::format("|{:10d}|\n", a);  // |        99|
std::cout << std::format("|{:#10o}|\n", a); // |      0143|
std::cout << std::format("|{:#10x}|\n", a); // |      0x63|
std::cout << std::format("|{:#10X}|\n", a); // |      0X63|

  字符串的示例如下:

std::string s { "ProCpp" };
std::cout << std::format("|{:_^10}|\n", s); // |__ProCpp__|

2.6 precision

  precision只能用于浮点和字符串类型。它的格式为一个点后跟浮点类型要输出的小数点位数(e、f、a),或整个浮点类型包含的有效数字个数(g),或字符串要输出的字符数。就像width一样,也可以是另一组花括号,在这种情况下,它被称为动态精度。precision取自format()的实参列表中的下一个实参或具有给定索引的实参。
  浮点型的示例如下:

double d{ 3.1415 / 2.3 };
std::string s{ "Hello, World!" };
std::cout << std::format("|{:12.2g}|\n", d);                          // |         1.4| 
std::cout << std::format("|{:12.3e}|\n", d);                          // |   1.366e+00|
std::cout << std::format("|{:12.2a}|\n", d);                          // |     1.5ep+0|

int width{ 12 };
int precision{ 3 };
std::cout << std::format("|{2:{0}.{1}f}|\n", width, precision, d);    // |       1.366|

2.7 0

  0表示,对于数值,将0插入格式化结果中,以达到[width]指定的最小宽度。这些0插在数值前面,但在符号以及任何0x、0X、0b或0B前缀之后。如果指定了align,则将忽略本选项。
示例如下:

int i { 42 };
double f { 3.1415 / 2.3 };
std::cout << std::format("|{:06d}|\n", i);     // |000042|
std::cout << std::format("|{:+06d}|\n", i);    // |+00042|
std::cout << std::format("|{:06X}|\n", i);     // |00002A|
std::cout << std::format("|{:#06X}|\n", i);    // |0X002A|
std::cout << std::format("|{:#06o}|\n", i);    // |000052|
std::cout << std::format("|{:012.3f}|\n", f);  // |00000001.366|

参考

[比] 马克·格雷戈勒著 程序喵大人 惠惠 墨梵 译 C++20高级编程(第五版)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值