C++ 进阶(22):格式化库——format 深入、自定义格式化、性能
更新时间:2026-09-01。本文是
languages/cpp/intermediate/进阶第 22 篇,接 filesystem 文件系统。C++20 引入了<format>库,基于 Python 的 format 语法。类型安全、可读性好、性能接近 printf。C++23 又加了std::print。
本文要回答的问题
- format 的基本语法是什么?
{}占位符怎么用? - 格式说明符(对齐、填充、精度、进制)怎么用?
- 自定义类型怎么支持 format?
- 性能和 printf、iostream 比怎么样?
一、基本用法
cpp
#include <format>
#include <print> // C++23
// 基本格式化
auto s = std::format("Hello, {}!", "world");
// "Hello, world!"
// 多个参数
auto s = std::format("{} + {} = {}", 1, 2, 3);
// "1 + 2 = 3"
// 位置参数
auto s = std::format("{1}, {0}, {1}", "a", "b");
// "b, a, b"
// C++23 print 直接输出
std::print("Hello, {}!\n", "world");
std::println("Pi is {}", 3.14159);二、格式说明符
cpp
// 宽度和对齐
std::format("{:10}", "hello"); // "hello "(左对齐,默认)
std::format("{:<10}", "hello"); // "hello "(左对齐)
std::format("{:>10}", "hello"); // " hello"(右对齐)
std::format("{:^10}", "hello"); // " hello "(居中)
std::format("{:*^10}", "hello"); // "**hello***"(填充字符)
// 数字格式
std::format("{:d}", 42); // "42"(十进制)
std::format("{:x}", 255); // "ff"(十六进制小写)
std::format("{:X}", 255); // "FF"(十六进制大写)
std::format("{:o}", 255); // "377"(八进制)
std::format("{:b}", 255); // "11111111"(二进制,C++23)
// 浮点数
std::format("{:.2f}", 3.14159); // "3.14"(2 位小数)
std::format("{:.0f}", 3.14); // "3"(0 位小数)
std::format("{:.3e}", 3.14159); // "3.142e+00"(科学计数法)
std::format("{:.3g}", 3.14159); // "3.14"(有效数字)
// 前导零
std::format("{:05d}", 42); // "00042"
std::format("{:#x}", 255); // "0xff"(带 0x 前缀)
std::format("{:#X}", 255); // "0xFF"
// 正负号
std::format("{:+d}", 42); // "+42"
std::format("{: d}", 42); // " 42"(正数前加空格)三、自定义类型格式化
cpp
struct Point {
int x, y;
};
// 特化 std::formatter
template<>
struct std::formatter<Point> {
char presentation = 'd'; // 默认格式
// 解析格式说明符
constexpr auto parse(auto& ctx) {
auto it = ctx.begin();
if (it != ctx.end() && (*it == 'd' || *it == 'x')) {
presentation = *it;
++it;
}
if (it != ctx.end() && *it != '}') {
throw std::format_error("invalid format for Point");
}
return it;
}
// 格式化输出
auto format(const Point& p, auto& ctx) const {
if (presentation == 'x') {
return std::format_to(ctx.out(), "({:#x}, {:#x})", p.x, p.y);
}
return std::format_to(ctx.out(), "({}, {})", p.x, p.y);
}
};
// 使用
Point p{10, 20};
std::println("{:d}", p); // "(10, 20)"
std::println("{:x}", p); // "(0xa, 0x14)"四、性能对比
| 方法 | 速度 | 类型安全 | 可读性 |
|---|---|---|---|
| printf | 最快 | ❌ 不安全 | ❌ 差 |
| iostream | 慢 | ✅ 安全 | ❌ 差 |
| format | 接近 printf | ✅ 安全 | ✅ 好 |
cpp
// format 的性能优化:用 format_to 避免分配
std::string buf;
std::format_to(std::back_inserter(buf), "{}", 42);
// 比 format 返回 string 少一次分配五、常见坑对照
| 坑 | 现象 | 对策 |
|---|---|---|
| 格式说明符写错 | 编译错误或 format_error | 检查说明符语法 |
| 参数个数不匹配 | 编译错误 | 保证 {} 数量匹配 |
| 浮点数精度位数 | 输出格式不对 | 用 .2f 指定小数位数 |
| C++17 没有 format | 找不到头文件 | 用 fmt 库或升级到 C++20 |
相关与延伸
下一篇:随机数库——random 深入;C 的 printf 格式化,见 C printf 格式化。
一句话总结
C++ format 库:std::format 类型安全、可读性好、性能接近 printf;{:10} 宽度对齐,{:.2f} 浮点数精度,{:x} 十六进制,{:#x} 带 0x 前缀;自定义类型特化 std::formatter 实现 parse 和 format;C++23 的 std::print 直接输出;format_to 避免分配,性能更好。