在 C++20 标准中引入的 std::format 为字符串格式化提供了类似 Python f-strings 的语法,让打印表格变得既简洁又强大。下面从基础语法、宽度对齐、列分隔符以及自定义格式化器等角度,逐步演示如何使用 std::format 构造多行、多列的表格。
1. 基础语法回顾
std::format 的调用方式类似:
#include <format>
#include <string>
std::string s = std::format("{:>10} | {:>5}", name, age);
{}内可以放置格式说明符,例如>表示右对齐,数字表示宽度,.2f表示浮点数保留两位小数等。
小贴士:
std::format需要 C++20 编译器支持,若使用 GCC 或 Clang 需要开启-std=c++20选项。
2. 打印简单表格
假设我们想打印学生成绩表格,包含姓名、学号和分数三列:
#include <format>
#include <vector>
#include <iostream>
struct Student {
std::string name;
int id;
double score;
};
int main() {
std::vector <Student> students = {
{"张三", 1001, 95.3},
{"李四", 1002, 88.75},
{"王五", 1003, 72.4}
};
// 打印表头
std::cout << std::format("{:10} | {:>5} | {:>7}\n", "姓名", "学号", "分数");
std::cout << std::string(10, '-') << "-+-" << std::string(5, '-') << "-+-" << std::string(7, '-') << '\n';
// 打印数据行
for (const auto& s : students) {
std::cout << std::format("{:10} | {:>5} | {:>7.2f}\n", s.name, s.id, s.score);
}
}
运行结果:
姓名 | 学号 | 分数
----------+-------+-------
张三 | 1001 | 95.30
李四 | 1002 | 88.75
王五 | 1003 | 72.40
3. 动态列宽与自适应格式
如果列宽不是固定的,而是根据内容自动决定,可以先遍历一次数据,计算最大宽度:
size_t name_w = std::strlen("姓名");
size_t id_w = std::strlen("学号");
size_t score_w = std::strlen("分数");
for (const auto& s : students) {
name_w = std::max(name_w, s.name.size());
id_w = std::max(id_w, std::to_string(s.id).size());
score_w = std::max(score_w, static_cast <size_t>(std::to_string(s.score).size()));
}
然后在 std::format 中使用这些宽度:
auto header = std::format("{:<{}} | {:>{}} | {:>{}.2f}\n", "姓名", name_w, "学号", id_w, "分数", score_w);
{:<{}} 说明:左对齐,占用 name_w 个字符。
4. 通过列分隔符绘制更精美的表格
如果想让表格更像 Markdown 或 Markdown 风格的表格,可以使用 | 和 - 分隔符,并在表头下面加一行连字符:
// 生成分隔行
std::string sep = std::string(name_w, '-') + "-+-" + std::string(id_w, '-') + "-+-" + std::string(score_w, '-');
// 打印
std::cout << header;
std::cout << sep << '\n';
for (const auto& s : students) {
std::cout << std::format("{:<{}} | {:>{}} | {:>{}.2f}\n", s.name, name_w, s.id, id_w, s.score, score_w);
}
5. 自定义格式化器(可选)
如果你想让表格中的数字以千位分隔符显示,或者把分数转换成等级(如 A/B/C),可以实现一个自定义格式化器:
#include <format>
#include <locale>
template<typename T>
struct thousand_separator {
T value;
};
template<typename T>
struct std::formatter<thousand_separator<T>> : std::formatter<T> {
template<typename FormatContext>
auto format(thousand_separator <T> s, FormatContext ctx) {
std::ostringstream oss;
oss.imbue(std::locale("en_US.UTF-8")); // 使用千位分隔符
oss << s.value;
return std::formatter <T>::format(oss.str(), ctx);
}
};
int main() {
std::cout << std::format("{:10}", thousand_separator{1234567}) << '\n'; // 输出 1,234,567
}
将其应用到表格即可:
std::cout << std::format("{:<{}} | {:>{}} | {:>{}.2f}\n",
s.name, name_w,
thousand_separator{s.id}, id_w,
s.score, score_w);
6. 小结
std::format让字符串格式化变得灵活,可直接在花括号内指定对齐、宽度、精度等。- 通过遍历一次数据计算最大列宽,可生成自适应宽度的表格。
- 使用
|、-等字符即可轻松构造 Markdown 风格表格。 - 自定义格式化器可进一步提升表格显示效果(千位分隔符、分数等级转换等)。
掌握这些技巧后,你就可以在 C++20 项目中快速生成清晰、可读性高的控制台表格,提升调试与报告打印的效率。