好的,各位朋友,欢迎来到“C++ std::format
:C++20 安全、高效的文本格式化”讲座现场!今天咱们就来好好聊聊C++20引入的这个神器——std::format
。
开场白:告别printf
,拥抱现代C++
相信大家都或多或少用过C语言时代的printf
函数,它确实很强大,能把各种数据按照指定的格式打印出来。但是,老朋友也有老朋友的烦恼,printf
最让人头疼的就是类型安全问题。一不小心,格式字符串和参数类型不匹配,轻则输出乱码,重则程序崩溃!
int age = 30;
printf("我今年%s岁了。n", age); // 绝对的灾难!
上面这段代码,格式字符串用%s
来表示字符串,但实际传入的是int
类型的age
,编译器不会报错,但运行起来就等着崩溃吧。
C++也提供了std::iostream
,但它的格式化操作符(<<
)用起来略显繁琐,而且自定义格式化也比较麻烦。
所以,C++20横空出世,带来了std::format
,它既有printf
的强大,又解决了类型安全问题,还拥有更好的性能。这简直就是程序员的福音啊!
std::format
:闪亮登场!
std::format
位于<format>
头文件中,使用起来非常简单。它使用花括号{}
作为占位符,并在花括号内指定格式。
最简单的例子:
#include <iostream>
#include <format>
int main() {
std::string name = "张三";
int age = 30;
std::cout << std::format("你好,我是{},今年{}岁。n", name, age);
return 0;
}
输出结果:
你好,我是张三,今年30岁。
看到了吗?std::format
会自动根据参数的类型进行格式化,不需要我们手动指定类型,也不用担心类型不匹配的问题。
格式化说明符:精雕细琢你的输出
std::format
的强大之处在于它提供了丰富的格式化说明符,可以对输出进行精细的控制。格式化说明符放在花括号{}
内,冒号:
之后。
1. 填充与对齐
- 填充字符: 可以指定一个字符来填充空白区域,例如空格、
0
等。 - 对齐方式:
<
:左对齐>
:右对齐^
:居中对齐
#include <iostream>
#include <format>
int main() {
int num = 42;
std::cout << std::format("{:5d}n", num); // 默认右对齐,填充空格
std::cout << std::format("{:<5d}n", num); // 左对齐,填充空格
std::cout << std::format("{:>5d}n", num); // 右对齐,填充空格
std::cout << std::format("{:^5d}n", num); // 居中对齐,填充空格
std::cout << std::format("{:05d}n", num); // 右对齐,填充0
std::cout << std::format("{:*^5d}n", num); // 居中对齐,填充*
return 0;
}
输出结果:
42
42
42
42
00042
**42*
2. 数字格式化
-
整数类型:
d
:十进制整数(默认)b
:二进制整数o
:八进制整数x
:十六进制整数(小写字母)X
:十六进制整数(大写字母)
-
浮点数类型:
f
:定点表示法e
:科学计数法(小写字母)E
:科学计数法(大写字母)g
:通用格式(自动选择f
或e
)G
:通用格式(自动选择f
或E
)%
:百分比表示法
-
精度: 使用
.
后跟数字来指定精度。对于浮点数,表示小数点后的位数;对于整数,表示最小位数。 -
千位分隔符: 使用
,
可以添加千位分隔符。
#include <iostream>
#include <format>
#include <cmath>
int main() {
int num = 255;
double pi = std::numbers::pi;
std::cout << std::format("十进制:{}n", num);
std::cout << std::format("二进制:{:b}n", num);
std::cout << std::format("八进制:{:o}n", num);
std::cout << std::format("十六进制(小写):{:x}n", num);
std::cout << std::format("十六进制(大写):{:X}n", num);
std::cout << std::format("Pi: {}n", pi);
std::cout << std::format("Pi: {:.2f}n", pi); // 保留两位小数
std::cout << std::format("Pi: {:e}n", pi); // 科学计数法
std::cout << std::format("Pi: {:.2e}n", pi); // 科学计数法,保留两位小数
std::cout << std::format("Pi: {:g}n", pi); // 通用格式
int large_num = 1234567;
std::cout << std::format("带千位分隔符:{:,d}n", large_num);
double percentage = 0.85;
std::cout << std::format("百分比:{:.2%}n", percentage); // 转换为百分比,保留两位小数
return 0;
}
输出结果:
十进制:255
二进制:11111111
八进制:377
十六进制(小写):ff
十六进制(大写):FF
Pi: 3.14159
Pi: 3.14
Pi: 3.141593e+00
Pi: 3.14e+00
Pi: 3.14159
带千位分隔符:1,234,567
百分比:85.00%
3. 字符与字符串格式化
s
:字符串(默认)c
:字符
#include <iostream>
#include <format>
int main() {
char ch = 'A';
std::string str = "Hello";
std::cout << std::format("字符:{}n", ch);
std::cout << std::format("字符串:{}n", str);
std::cout << std::format("字符串(宽度):{:10}n", str); // 宽度为10,默认右对齐
std::cout << std::format("字符串(左对齐):{:<10}n", str); // 宽度为10,左对齐
return 0;
}
输出结果:
字符:A
字符串:Hello
字符串(宽度): Hello
字符串(左对齐):Hello
4. 日期与时间格式化
std::format
也支持日期和时间的格式化,需要包含<chrono>
头文件。但是,C++20对chrono的格式化支持比较有限,C++23将会引入更强大的支持。这里简单演示一下:
#include <iostream>
#include <format>
#include <chrono>
int main() {
auto now = std::chrono::system_clock::now();
auto time_point = std::chrono::system_clock::to_time_t(now);
std::tm local_time = *std::localtime(&time_point); // 转换为本地时间
std::cout << std::format("日期时间:{:%Y-%m-%d %H:%M:%S}n", local_time);
return 0;
}
输出结果(取决于当前时间):
日期时间:2023-10-27 10:30:00
注意: chrono
的格式化说明符以%
开头,例如%Y
表示年份,%m
表示月份,%d
表示日期,%H
表示小时,%M
表示分钟,%S
表示秒。
std::vformat
:灵活的格式化
std::vformat
是std::format
的底层函数,它接受一个std::format_args
对象作为参数,这个对象包含了要格式化的参数列表。std::format
实际上就是调用std::vformat
来实现格式化的。
使用std::vformat
可以实现更灵活的格式化,例如在运行时动态构建格式字符串。
#include <iostream>
#include <format>
std::string format_message(const std::string& format_str, std::format_args args) {
return std::vformat(format_str, args);
}
int main() {
std::string name = "李四";
int score = 95;
std::string message = format_message("学生{}的成绩是{}分。n", std::make_format_args(name, score));
std::cout << message;
return 0;
}
输出结果:
学生李四的成绩是95分。
性能:std::format
的优势
std::format
在性能方面通常优于printf
和std::iostream
。
- 编译时检查:
std::format
可以在编译时检查格式字符串的有效性,避免运行时错误。 - 减少内存分配:
std::format
通常可以避免不必要的内存分配,提高性能。 - 优化实现: 现代C++编译器的
std::format
实现都经过了高度优化。
总结:std::format
,你值得拥有!
std::format
是C++20引入的一个强大的文本格式化工具,它具有以下优点:
- 类型安全: 避免了
printf
的类型安全问题。 - 简单易用: 使用花括号
{}
作为占位符,语法简洁明了。 - 功能强大: 提供了丰富的格式化说明符,可以对输出进行精细的控制。
- 性能优秀: 通常优于
printf
和std::iostream
。 - 可扩展性: 可以自定义格式化器来支持自定义类型。
特性 | printf |
std::iostream |
std::format |
---|---|---|---|
类型安全 | 否 | 部分支持 | 是 |
语法 | 复杂 | 繁琐 | 简洁 |
功能 | 强大 | 一般 | 强大 |
性能 | 较好 | 一般 | 优秀 |
可扩展性 | 差 | 较好 | 好 |
编译时检查 | 否 | 否 | 是 |
总而言之,std::format
是现代C++文本格式化的首选方案。如果你还在使用printf
或者觉得std::iostream
不够方便,那么赶紧拥抱std::format
吧!它会让你写出更安全、更高效、更优雅的代码。
扩展:自定义格式化
std::format
还支持自定义类型的格式化。你需要为你的类型提供一个格式化器,这个格式化器负责将你的类型转换为字符串。这部分比较高级,我们这里简单提一下,以后有机会再详细讲解。
结束语:感谢聆听!
好了,今天的“C++ std::format
:C++20 安全、高效的文本格式化”讲座就到这里了。希望大家通过今天的学习,能够掌握std::format
的基本用法,并在实际项目中灵活运用它。感谢各位的聆听,我们下次再见!希望大家多多练习,熟能生巧!记住,编码的乐趣在于不断探索和学习!