- 数据库
- 教育
- 后端
【免费下载链接】bustub
The BusTub Relational Database Management System (Educational)
libfort 是一款用 C 编写的跨平台格式化文本表格库,默认情况下表中所有单元格共享同一套属性。本文以官方教程 Cell-and-table-properties.md 为主线,系统讲解单元格属性(Cell Properties)与表格属性(Table Properties)的完整 API、全部可选值、属性继承的底层实现,并结合仓库内fort.c/fort.h源码、单元测试以及 BusTub 数据库 Shell 的真实使用场景,给出可直接复制运行的完整示例。读完本文,你将能够精确控制表头、对齐、内边距、列宽、颜色、文本样式与表格外边距,并理解 libfort 属性查询的分层回退逻辑。
1. 属性体系总览:默认属性与逐表覆盖
libfort 的属性体系采用"全局默认 + 逐表/逐单元格覆盖"的两级模型:
- 所有通过
ft_create_table()新建的表格,其单元格默认拥有完全相同的属性。例如想让第一行成为表头(header),必须显式设置对应属性。 - 通过
ft_set_default_cell_prop/ft_set_default_tbl_prop可以修改未来所有新表的默认属性; - 通过
ft_set_cell_prop/ft_set_tbl_prop可以修改某个具体表格的属性。
/* 修改所有新建表格的默认单元格属性 */ int ft_set_default_cell_prop(uint32_t property, int value); /* 修改特定表格的单元格属性 */ int ft_set_cell_prop(ft_table_t *table, size_t row, size_t col, uint32_t property, int value);/* 修改所有新建表格的默认表格属性 */ int ft_set_default_tbl_prop(uint32_t property, int value); /* 修改特定表格的表格属性 */ int ft_set_tbl_prop(ft_table_t *table, uint32_t property, int value);四个函数均返回int:成功返回0(即FT_SUCCESS),失败返回负整数。property是属性标识符(位标志),value是属性取值。
1.1 源码中的全局默认值
在 fort.c 中,全局默认单元格属性g_default_cell_properties定义如下(可作为默认行为的权威参考):
| 属性 | 默认值 |
|---|---|
最小宽度col_min_width | 0 |
文本对齐align | FT_ALIGNED_LEFT(左对齐) |
上/下内边距cell_padding_top/bottom | 0 |
左/右内边距cell_padding_left/right | 1 |
空串高度cell_empty_string_height | 1 |
行类型row_type | FT_ROW_COMMON |
| 前景/背景色 | FT_COLOR_DEFAULT |
| 文本样式 | FT_TSTYLE_DEFAULT |
即:默认新建表格是左对齐、左右各 1 个字符内边距、无颜色无样式、普通行的经典 ASCII 表格。
2. 单元格属性(Cell Properties)
2.1 定位宏:批量设置一行、一列或整表
ft_set_cell_prop的row/col参数除具体下标外,还可使用特殊宏(定义见 fort.h):
#define FT_ANY_COLUMN (UINT_MAX) /* 任意列:可代表一行中的所有单元格 */ #define FT_CUR_COLUMN (UINT_MAX - 1) /* 当前列 */ #define FT_ANY_ROW (UINT_MAX) /* 任意行:可代表一列中的所有单元格 */ #define FT_CUR_ROW (UINT_MAX - 1) /* 当前行 */因此ft_set_cell_prop天然支持三种批量作用域:
| 调用示例 | 作用范围 |
|---|---|
ft_set_cell_prop(table, row, FT_ANY_COLUMN, property, value) | 设置该行所有单元格的属性 |
ft_set_cell_prop(table, FT_ANY_ROW, col, property, value) | 设置该列所有单元格的属性 |
ft_set_cell_prop(table, FT_ANY_ROW, FT_ANY_COLUMN, property, value) | 设置整张表所有单元格的属性 |
此外FT_CUR_ROW/FT_CUR_COLUMN表示"当前写入位置"(由ft_set_cur_cell设定,默认随写入自动推进),在链式写入场景中很实用;C++ 封装内部就用到了它们(见第 5 节)。
2.2 完整示例:表头、右对齐、最小宽度与列内边距
以下示例完整演示了上述三种作用域(来自 Cell-and-table-properties.md):
ft_table_t *table = ft_create_table(); /* 第 0 行整行设置为表头 */ ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER); ft_write_ln(table, "N", "Driver", "Time", "Avg Speed"); ft_write_ln(table, "1", "Ricciardo", "1:25.945", "222.128"); ft_write_ln(table, "2", "Hamilton", "1:26.373", "221.027"); ft_write_ln(table, "3", "Verstappen", "1:26.469", "220.782"); /* 单元格 (0, 1) 右对齐 */ ft_set_cell_prop(table, 0, 1, FT_CPROP_TEXT_ALIGN, FT_ALIGNED_RIGHT); /* 单元格 (0, 1) 最小宽度为 30 */ ft_set_cell_prop(table, 0, 1, FT_CPROP_MIN_WIDTH, 30); /* 第 3 列所有单元格左内边距 = 3 */ ft_set_cell_prop(table, FT_ANY_ROW, 3, FT_CPROP_LEFT_PADDING, 3); printf("%s\n", ft_to_string(table)); ft_destroy_table(table);输出结果:
+---+------------------------------+----------+-------------+ | N | Driver | Time | Avg Speed | +---+------------------------------+----------+-------------+ | 1 | Ricciardo | 1:25.945 | 222.128 | | 2 | Hamilton | 1:26.373 | 221.027 | | 3 | Verstappen | 1:26.469 | 220.782 | +---+------------------------------+----------+-------------+要点解读:
- 第 0 行被声明为
FT_ROW_HEADER后,输出中出现了表头分隔线,且表头行样式独立; FT_CPROP_MIN_WIDTH = 30使 "Driver" 列宽被拉宽,超出内容Ricciardo的长度,因此第 2、3 行的Ricciardo等名字左对齐补齐空白;- 第 3 列(
Avg Speed列)的左内边距为 3,输出中222.128前明显多出空格; - 单元格 (0, 1) 的右对齐使
Driver表头靠右显示。
2.3 单元格属性一览表
libfort 支持的单元格属性标识符定义在 fort.h(均为独立的位标志0x01U << n):
| 属性 | 说明 | 可选值 |
|---|---|---|
FT_CPROP_MIN_WIDTH | 最小宽度 | 非负整数 |
FT_CPROP_TEXT_ALIGN | 文本对齐 | enum ft_text_alignment |
FT_CPROP_TOP_PADDING | 内容上内边距 | 非负整数 |
FT_CPROP_BOTTOM_PADDING | 内容下内边距 | 非负整数 |
FT_CPROP_LEFT_PADDING | 内容左内边距 | 非负整数 |
FT_CPROP_RIGHT_PADDING | 内容右内边距 | 非负整数 |
FT_CPROP_EMPTY_STR_HEIGHT | 空单元格高度(垂直占位行数) | 非负整数 |
FT_CPROP_ROW_TYPE | 行类型 | enum ft_row_type |
FT_CPROP_CONT_FG_COLOR | 内容前景色 | enum ft_color |
FT_CPROP_CELL_BG_COLOR | 单元格背景色 | enum ft_color |
FT_CPROP_CONT_BG_COLOR | 内容背景色 | enum ft_color |
FT_CPROP_CELL_TEXT_STYLE | 单元格文本样式 | enum ft_text_style |
FT_CPROP_CONT_TEXT_STYLE | 内容文本样式 | enum ft_text_style |
注意:原教程表中将
FT_CPROP_EMPTY_STR_HEIGHT误写为 "Right padding for cell content",其正确语义为空单元格的高度(fort.h 中注释为 "Height of empty cell");右内边距由FT_CPROP_RIGHT_PADDING负责,该属性在原教程表中被遗漏,上表已按源码补齐。
2.4 属性取值枚举
enum ft_text_alignment { FT_ALIGNED_LEFT = 0, /* 左对齐 */ FT_ALIGNED_CENTER, /* 居中 */ FT_ALIGNED_RIGHT /* 右对齐 */ }; enum ft_row_type { FT_ROW_COMMON = 0, /* 普通行 */ FT_ROW_HEADER /* 表头行 */ }; enum ft_color { FT_COLOR_DEFAULT = 0, FT_COLOR_BLACK = 1, FT_COLOR_RED = 2, FT_COLOR_GREEN = 3, FT_COLOR_YELLOW = 4, FT_COLOR_BLUE = 5, FT_COLOR_MAGENTA = 6, FT_COLOR_CYAN = 7, FT_COLOR_LIGHT_GRAY = 8, FT_COLOR_DARK_GRAY = 9, FT_COLOR_LIGHT_RED = 10, FT_COLOR_LIGHT_GREEN = 11, FT_COLOR_LIGHT_YELLOW = 12, FT_COLOR_LIGHT_BLUE = 13, FT_COLOR_LIGHT_MAGENTA = 14, FT_COLOR_LIGHT_CYAN = 15, FT_COLOR_LIGHT_WHYTE = 16 }; enum ft_text_style { FT_TSTYLE_DEFAULT = (1U << 0), /* 默认样式 */ FT_TSTYLE_BOLD = (1U << 1), /* 加粗 */ FT_TSTYLE_DIM = (1U << 2), /* 暗色 */ FT_TSTYLE_ITALIC = (1U << 3), /* 斜体 */ FT_TSTYLE_UNDERLINED = (1U << 4), /* 下划线 */ FT_TSTYLE_BLINK = (1U << 5), /* 闪烁 */ FT_TSTYLE_INVERTED = (1U << 6), /* 反色(前景/背景互换) */ FT_TSTYLE_HIDDEN = (1U << 7) /* 隐藏(可用于密码) */ };版本说明:原教程中
FT_COLOR_LIGHT_MAGENTA = 15、FT_COLOR_LIGHT_CYAN = 16、FT_COLOR_LIGHT_WHYTE = 17与当前仓库 fort.h 中的取值(依次为 14、15、16)不一致,本文以仓库源码为准。
文本样式可按位组合:FT_TSTYLE_*是位标志,在 fort.c 的set_cell_property_impl中,非FT_TSTYLE_DEFAULT的样式会与原值做按位或(|)合并,因此可以一次叠加多种样式;传入FT_TSTYLE_DEFAULT则重置。例如:
/* 同时设置加粗 + 下划线(通过两次调用叠加) */ ft_set_cell_prop(table, 0, 0, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_BOLD); ft_set_cell_prop(table, 0, 0, FT_CPROP_CONT_TEXT_STYLE, FT_TSTYLE_UNDERLINED);2.5 属性查询的继承层级(源码级原理)
当渲染某单元格 (row, col) 的属性时,libfort 并不是只查这一个格子,而是按照优先级逐层回退查找。核心逻辑位于 fort.c 的get_cell_property_hierarchically:
- 先查精确坐标
(row, col)的属性; - 未命中则查整行
(FT_ANY_ROW, col); - 仍未命中则查整列
(row, FT_ANY_COLUMN); - 再查整表
(FT_ANY_ROW, FT_ANY_COLUMN); - 全部未命中则回退到全局默认值
g_default_cell_properties。
这意味着:行级批量设置会被列级/单元格级设置覆盖,单元格级最优先。第 2.2 节示例中,单元格 (0, 1) 的右对齐与第 3 列的左内边距互不干扰,正是因为它们落在不同的属性维度上。
3. 表格属性(Table Properties)
3.1 API 与属性表
表格属性控制的是整张表的外观,使用ft_set_tbl_prop与ft_set_default_tbl_prop设置,属性标识符定义在 fort.h:
/* 修改所有新建表格的默认表格属性 */ int ft_set_default_tbl_prop(uint32_t property, int value); /* 修改特定表格的表格属性 */ int ft_set_tbl_prop(ft_table_t *table, uint32_t property, int value);| 属性 | 说明 | 可选值 |
|---|---|---|
FT_TPROP_LEFT_MARGIN | 左边距 | 非负整数 |
FT_TPROP_TOP_MARGIN | 上边距 | 非负整数 |
FT_TPROP_RIGHT_MARGIN | 右边距 | 非负整数 |
FT_TPROP_BOTTOM_MARGIN | 下边距 | 非负整数 |
3.2 边距的几何含义
表格边距仅在转换为字符串(ft_to_string/ C++ 的to_string)时生效,是表格外框之外的空白区域。原教程给出了清晰的示意图:
M ^ M L a | Top margin R a e r v i r f g ╔═══════╤════════╗ g g t i ║ Cell │ Cell ║ h i n ╠═══════╪════════╣ t n <----->║ Cell │ Cell ║<------------> ╚═══════╧════════╝ ^ | Bottom margin v用法示例:
ft_set_tbl_prop(table, FT_TPROP_LEFT_MARGIN, 5); /* 表格左侧空 5 个字符 */ ft_set_tbl_prop(table, FT_TPROP_TOP_MARGIN, 2); /* 表格上方空 2 行 */ ft_set_tbl_prop(table, FT_TPROP_RIGHT_MARGIN, 3); /* 表格右侧空 3 个字符 */ ft_set_tbl_prop(table, FT_TPROP_BOTTOM_MARGIN, 1); /* 表格下方空 1 行 */3.3 补充:FT_TPROP_ADDING_STRATEGY
除四个边距外,源码 fort.h 还定义了第 5 个表格属性FT_TPROP_ADDING_STRATEGY(添加策略),用于控制向非空单元格追加数据时的行为,取值见enum ft_adding_strategy(fort.h):
enum ft_adding_strategy { FT_STRATEGY_REPLACE = 0, /* 替换旧内容(默认策略) */ FT_STRATEGY_INSERT /* 插入新内容,旧内容后移 */ };默认是FT_STRATEGY_REPLACE;设为FT_STRATEGY_INSERT后,向已有内容的单元格写入会触发内容平移,此时ft_ln等操作也可能失败。
4. 错误处理与返回值
libfort 遵循标准 C 惯例:函数正常时返回0或指针,出错时返回负整数或NULL。返回码定义在 fort.h:
| 返回码 | 值 | 含义 |
|---|---|---|
FT_SUCCESS | 0 | 操作成功 |
FT_MEMORY_ERROR | -1 | 内存分配失败 |
FT_EINVAL | -2 | 非法参数 |
FT_INTERN_ERROR | -3 | libfort 内部逻辑错误(通常是库自身的 bug) |
FT_GEN_ERROR | -4 | 其他一般性错误 |
辅助判断宏:
#define FT_IS_SUCCESS(arg) ((arg) >= 0) #define FT_IS_ERROR(arg) ((arg) < 0)例如,属性设置函数会对非法取值做校验:在set_cell_property_impl(fort.c)中,宽度、内边距、空串高度等属性通过CHECK_NOT_NEGATIVE(value)检查,传入负值会返回FT_EINVAL;传入无法识别的property则返回FT_EINVAL。因此建议对每个ft_set_*_prop调用检查返回值:
if (FT_IS_ERROR(ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER))) { fprintf(stderr, "failed: %s\n", ft_strerror(-1)); /* 或直接处理错误码 */ }错误码对应的可读信息可通过ft_strerror(int error_code)获取(fort.c)。仓库测试 test_memory_errors.c 中即对ft_set_cell_prop的返回值做了严格断言。
5. C++ 封装与 BusTub 中的实际应用
5.1 C++ 包装:fort.hpp 的 property_owner
libfort 为 C++11 及以上项目提供了封装头文件 fort.hpp,核心类是fort::table(含char_table、utf8_table两个特化)。每个单元格属性在 C++ 侧都映射为链式友好的方法,例如set_min_width、set_text_align、set_top_padding等,其内部通过property_owner::set_property转发到 C API(fort.hpp):
bool set_property(uint32_t property, int value) { int status; if (set_default_properties_) { status = ft_set_default_cell_prop(property, value); /* 默认属性模式 */ } else { status = ft_set_cell_prop(ps_table_->table_, ps_row_idx_, ps_coll_idx_, property, value); } return FT_IS_SUCCESS(status); }值得一提的是,C++ 的operator<<流式操作符内部也直接复用了本文介绍的属性机制:table << fort::header实际执行的是ft_set_cell_prop(table_, FT_CUR_ROW, FT_ANY_ROW, FT_CPROP_ROW_TYPE, FT_ROW_HEADER),即把当前行整行标记为表头(fort.hpp)——这与第 2.2 节示例中ft_set_cell_prop(table, 0, FT_ANY_COLUMN, ...)是同一原理,只是把行号换成了"当前行"。
表格边距在 C++ 侧同样有对应方法(fort.hpp):
bool set_left_margin(unsigned value); /* 转发 FT_TPROP_LEFT_MARGIN */ bool set_top_margin(unsigned value); /* 转发 FT_TPROP_TOP_MARGIN */ bool set_right_margin(unsigned value); /* 转发 FT_TPROP_RIGHT_MARGIN */ bool set_bottom_margin(unsigned value); /* 转发 FT_TPROP_BOTTOM_MARGIN */5.2 BusTub Shell 如何用 libfort 渲染查询结果
BusTub 数据库的交互式 Shell(tools/shell/shell.cpp)直接引入libfort/lib/fort.hpp,并在启动时注册了 UTF-8 宽度计算函数以正确排版多字节字符:
#include "libfort/lib/fort.hpp" // ... ft_set_u8strwid_func(&GetWidthOfUtf8); /* 用 utf8proc 计算字符显示宽度 */查询结果的渲染由 bustub_instance.h 中的FortTableWriter完成,它把 SQL 执行结果流式写入fort::utf8_table:
class FortTableWriter : public ResultWriter { public: void WriteCell(const std::string &cell) override { table_ << cell; } void WriteHeaderCell(const std::string &cell) override { table_ << cell; } void BeginHeader() override { table_ << fort::header; } /* 当前行标记为表头 */ void EndHeader() override { table_ << fort::endr; } /* 换行 */ void BeginRow() override {} void EndRow() override { table_ << fort::endr; } void BeginTable(bool simplified_output) override { if (simplified_output) { table_.set_border_style(FT_EMPTY_STYLE); /* 简化输出时去掉边框 */ } } void EndTable() override { tables_.emplace_back(table_.to_string()); table_ = fort::utf8_table{}; } // ... fort::utf8_table table_; };主循环(shell.cpp)执行 SQL 后逐表打印:
auto writer = bustub::FortTableWriter(); bustub->ExecuteSql(query, writer); for (const auto &table : writer.tables_) { std::cout << table << std::flush; }可见:"第 0 行是表头"这个行为在 BusTub Shell 中就是通过fort::header操纵符 →ft_set_cell_prop(..., FT_CPROP_ROW_TYPE, FT_ROW_HEADER)实现的,这与本文第 2.2 节的 C API 示例完全同源。
6. 快速参考与进一步阅读
设置属性的通用套路:
- 用
ft_create_table()(C)或fort::table(C++)创建表格; - 需要全表统一风格时,优先用
ft_set_default_cell_prop/ft_set_default_tbl_prop设置全局默认; - 用
FT_ANY_ROW/FT_ANY_COLUMN做行、列级批量设置,用具体坐标做单元格级微调(精确坐标优先级最高); - 文本样式可多次调用叠加(按位或);传入负值或非法属性会得到
FT_EINVAL,务必检查返回值; - 最终用
ft_to_string(table)(C)或table.to_string()(C++)输出,表格边距在这一步生效; - 用完调用
ft_destroy_table(table)释放资源。
相关教程可继续阅读 Table-life-cycle.md(表格生命周期)、Filling-tables-with-data.md(写入数据)与 Border-styles.md(边框样式,BusTub 简化输出中使用的FT_EMPTY_STYLE即来自该体系);API 完整声明见 fort.h,C++ 封装见 fort.hpp,行为测试可参考 test_table_basic.c 与 test_memory_errors.c。
- 数据库
- 教育
- 后端
【免费下载链接】bustub
The BusTub Relational Database Management System (Educational)
相关推荐
BusTub 与 libfort:C++ 表单元素属性与表格属性设置完全指南
BusTub 与 libfort:C++ 表单元素属性与表格属性设置完全指南 在终端应用中把查询结果渲染成对齐美观的表格,是数据库 Shell 的常见需求。Bu
数据库教育后端BusTub 内置的 libfort 控制台表格库:C/C++ 完整 API 与 BusTub 实战集成指南
BusTub 内置的 libfort 控制台表格库:C/C++ 完整 API 与 BusTub 实战集成指南 libfort 是 BusTub(bustub)项
数据库教育后端libfort 表格边框样式完全指南:内置 Border Style 详解与 C++ API 实战(Bustub 项目视角)
libfort 表格边框样式完全指南:内置 Border Style 详解与 C++ API 实战(Bustub 项目视角) libfort 是一个轻量级的 C
数据库教育后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考