如何在 C++20 中使用 std::format 打印表格?

在 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 项目中快速生成清晰、可读性高的控制台表格,提升调试与报告打印的效率。

发表评论