在命令行工具里把数据整齐地排成表格,是很多C++小工具的常见需求。如果列内容长度不一,直接拼接字符串会让输出歪歪扭扭。通过提前统计每列所需显示宽度并自动补空格,可以用不到一百行代码写出一个轻量的表格打印函数。

为什么需要自动计算列宽
手写空格对齐的弊端很明显:一旦某行数据变长,之前调好的格式就失效了。更麻烦的是中文、全角符号在终端里占两个字符宽,而英文字母只占一个,用string的length去算一定会出错。自动计算列宽的核心,就是按“显示宽度”而不是“字节数”来测量每一格,再取每列最大值作为该列的标准宽度。
这样做之后,表格函数可以接收任意行数的数据,不需要调用者关心排版。后续如果想支持右对齐、居中或者导出到文件,只改格式化那一步就行,主体逻辑不用动。
测量显示宽度的关键函数
标准C++没有直接提供“显示宽度”接口,但在类Unix系统和Windows的较新控制台都支持wcwidth系列函数。它会返回宽字符的终端列宽,普通ASCII返回1,中文等返回2。我们把string先转成wstring,再逐字符累加。
下面这段代码封装了获取字符串显示宽度的逻辑,遇到无法识别的字符按1处理,保证不崩溃:
#include <string>
#include <cwchar>
#include <locale>
// 将utf-8 string转为wstring
std::wstring to_wide(const std::string& s) {
std::locale loc("en_US.UTF-8");
std::wstring result;
for (size_t i = 0; i < s.size(); ) {
wchar_t wc;
int len = std::mbtowc(&wc, s.c_str() + i, s.size() - i);
if (len <= 0) { i++; continue; }
result.push_back(wc);
i += len;
}
return result;
}
// 返回字符串在终端的显示宽度
int display_width(const std::string& s) {
std::wstring ws = to_wide(s);
int w = 0;
for (wchar_t c : ws) {
int cw = wcwidth(c);
if (cw < 0) cw = 1;
w += cw;
}
return w;
}
表格数据与列宽计算
我们用vector<vector<string>>保存表格内容,第一行通常当作表头。计算列宽时,先确定最大列数,再对每一列遍历所有行,取display_width的最大值。这样得到的widths数组就是每列应该占的字符数。
需要注意某些行可能缺列,访问前要判断列索引是否越界,避免vector下标异常。下面演示了计算逻辑:
#include <vector>
#include <algorithm>
std::vector<int> calc_column_widths(const std::vector<std::vector<std::string>>& table) {
size_t cols = 0;
for (const auto& row : table)
cols = std::max(cols, row.size());
std::vector<int> widths(cols, 0);
for (const auto& row : table) {
for (size_t c = 0; c < row.size(); ++c) {
int w = display_width(row[c]);
if (w > widths[c]) widths[c] = w;
}
}
return widths;
}
对齐输出实现
有了列宽,就能在每格后面补空格到指定宽度。左对齐直接补在右边;右对齐则在左边补空格。我们用stringstream拼每一行,列之间可加竖线分隔,最后统一打印。
下面的print_table函数支持左对齐,并在列间加了空格和竖线,你可以改造成右对齐或自定义填充符:
#include <sstream>
#include <iostream>
void print_table(const std::vector<std::vector<std::string>>& table) {
auto widths = calc_column_widths(table);
for (const auto& row : table) {
std::ostringstream line;
for (size_t c = 0; c < widths.size(); ++c) {
std::string cell = (c < row.size()) ? row[c] : "";
int pad = widths[c] - display_width(cell);
line << cell;
for (int i = 0; i < pad; ++i) line << ' ';
if (c + 1 < widths.size()) line << " | ";
}
std::cout << line.str() << std::endl;
}
}
完整使用示例
把上面的函数组合起来,就能输出整齐的表格。下面示例混入了中文和英文,验证宽度计算是否正确:
int main() {
std::vector<std::vector<std::string>> data = {
{"姓名", "年龄", "城市"},
{"张三", "28", "北京"},
{"Alice", "34", "Shanghai"},
{"王小明", "22", "广州"}
};
print_table(data);
return 0;
}
运行后各列会按最长内容的显示宽度对齐,中文不再错位。若需要右对齐数字列,只需在补空格时把循环放到cell前面即可。这种实现零依赖、易嵌入,适合日志、监控和小型CLI工具直接复用。