1 C函数printf
最常见的是C语言提供的printf
函数,可以在格式化字符串中设置打印的各种类型、进制和精度等。这里不再细讲了。
2 std::cout+属性设置函数
cout是C++默认的打印函数,可以支持各个基本类型的打印。我们可以通过**<ios>**
和**<iomanip>**
库中的函数设置cout的打印属性,达到与printf格式化字符串一样的功能。当然它们也可以用于std::cin。常用的函数有:
- 设置整数输入输出进制:std::dec, std::hex, std::oct
- 修改浮点输入/输出的默认格式:std::fixed, std::scientific, std::hexfloat, std::defaultfloat
- 设置打印精度:std::setprecision(n)
- 设置打印长度:std::setw(n)
示例
//5位长度,两位精度,即小数点后一位
std::cout << "float number is " << std::setw(5)
<< std::setprecision(2) << 5.13 << endl;
3 std::format()
在C++20引入了<format>
库,我们可以用std::format()
代替那么多属性设置函数,达到和printf
格式化字符串类似的功能。std::format
使用**{}**
标识一个需要被打印的元素,format最终返回一个格式化后的string类型,可以交给std::cout
打印出来。
基于属性设置
可以在{}中设置如下的属性:
{[index]:[[fill]align][sign][#][0][width][.precision][type]}
- index:打印顺序,指定此处使用后面哪个参数
- fill:设置对齐方式之后的填充字符
- 默认:空白字符
- align:对齐方式:
- 左对齐:<
- 右对齐:>
- 中间对齐:^
- sign:设置在正数前面打印什么字符,一般为+或空白
:表示替代格式,用于常见的数值类型,在value前添加0x、0b、0等进制标志
- 0:对齐的填充字符为数字0,不使用fill中字符
- width:设置打印长度
- .precision:设置精度
- type:打印参数的类型
- 浮点数类型:
- f:浮点数
- e:科学计数法
- g:通用格式
- 整数类型:
- x:十六进制
- b:二进制
- o:八进制
- d:十进制
- bool类型:
- s:true 、false
- b、c、d、o、x:1、0
- c:char
- s:string
- p:指针地址0x
- 浮点数类型:
需要注意:有些属性并不适用于某些类型,假如在{}中设置了不符合的属性,fotmat函数会抛出异常std::format_error
。
示例如下:
std::cout << std::format("{:*<7}|{:*<7}|{:*>7}|{:*>7}|{:*>7}\n", 1,-.2,"str",'c',true);
// Centered alignment + 0 formatting option for numbers
std::cout << std::format("{:^07}|{:^07}|{:^7}|{:^7}|{:^7}\n", 1, -.2, "str", 'c', true);
//输出为
//1******|-0.2***|****str|******c|***true
//0000001|-0000.2| str | c | true
std::cout << std::format("Default: {:.2}, fixed: {:.2f}, scientific: {:.2e}, "
"general: {:.2g}\n", pi, pi, pi, pi);
std::cout << std::format("Default: {}, binary: {:b}, hex.: {:x}\n", 314, 314, 314);
//输出为
//Default: 3.1, fixed: 3.14, scientific: 3.14e+00, general: 3.1
//Default: 314, binary: 100111010, hex.: 13a
自定义类型的format支持
std::formatter
模板类用于针对某类型实现format打印的支持,目前C++的基本类型和大部分库类型都有相应的formatter实现。
formatter有两个成员模板函数,分别为:
- parse:解析format属性,默认是上面的统一格式,也可以添加自己额外的属性规则
- format:根据属性设置,修改输入的类型以达到格式化的效果
下面是一个示例:
#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) {
return std::formatter<T, CharT>::format(t.value, fc);
}
};
int main()
{
Box<int> v = { 42 };
std::cout << std::format("{:#x}", v);
}