cmark需启用CMARK_OPT_TABLES标志并手动遍历AST获取表格结构,因默认不识别表格节点;导出CSV须转义换行、逗号和双引号,std::quoted不满足要求,需自定义escape函数处理内部双引号和换行符。

用 libmarkdown 或 cmark 解析 Markdown 表格时,为什么拿不到表格结构?
因为标准 C++ 没有原生 Markdown 解析器,libmarkdown(如 discount)默认只输出 HTML,不暴露 AST;cmark 虽提供 AST 接口,但表格节点(CMARK_NODE_TABLE)需手动遍历子节点,且文档里没写清楚字段顺序和单元格合并逻辑。直接调 cmark_render_html 会丢掉行列关系,导出 CSV 就断链了。
- 必须启用
cmark_parse_document并传入CMARK_OPT_TABLES标志,否则CMARK_NODE_TABLE根本不会被识别 - 表格行(
CMARK_NODE_TABLE_ROW)的子节点是单元格(CMARK_NODE_TABLE_CELL),但每个单元格内还有文本子节点,得递归取CMARK_NODE_TEXT的 literal 字段 -
cmark_node_get_literal对非文本节点返回nullptr,直接解引用会崩溃——得先用cmark_node_get_type判断类型
手写正则提取 Markdown 表格行是否靠谱?
对简单表格(无换行、无转义竖线、无嵌套)可以,但遇到 | `code|` | 或
| cell with <br> line break |
就失效。更麻烦的是表头分隔行(|---|---|)和内容行格式一致,仅靠正则无法区分语义。
- 推荐用
std::regex配合std::sregex_iterator提取每行的列,但只用于 PoC 或预处理:匹配R"(\|([^|]*)\|)"会漏掉首尾空格和转义符 - 真正稳定的做法是先用
cmark把 Markdown 转成带位置信息的 AST,再按node->start_line分组“视觉行”,比纯文本解析可靠得多 - 如果项目不允许引入 C 库,可改用
boost::spirit写轻量 parser,但开发成本远高于链接cmark
导出 CSV 时如何处理换行、逗号和双引号?
Markdown 表格单元格里的换行(\n)在 CSV 中必须包裹在双引号内,且已有双引号要变成两个(" → "")。C++ 标准库没 CSV encoder,自己拼接容易出错。
- 别用
std::quoted直接包字符串——它不处理内部双引号转义,也不加换行符前后的引号 - 安全做法是写一个简易 escape 函数:
if (cell.find_first_of("\",\n\r") != std::string::npos) { out - 注意:CSV 规范要求所有字段都用逗号分隔,但空列不能省略——
| a || c |必须导出为"a",,"c",不能跳过中间空字段
完整流程中哪些环节最容易漏掉?
最常卡在三处:一是忘记 cmark 初始化(cmark_version() 调一次即可,但不调会导致后续解析失败);二是把 CMARK_NODE_TABLE_CELL 当作叶子节点,忽略它可能含强调、链接等内联节点;三是导出多张表时没重置 CSV 输出缓冲,导致表头混进第二张表第一行。
- 每张
CMARK_NODE_TABLE节点下,先遍历一次CMARK_NODE_TABLE_ROW取表头(通常第一行),再遍历剩余行取数据——不能假设所有表都有表头 - 单元格文本提取必须递归:从
cell开始,对每个子节点判断类型,遇到CMARK_NODE_TEXT就取literal,遇到CMARK_NODE_EMPH就继续递归其子节点 - Windows 下写 CSV 文件记得用
std::ofstream f("out.csv", std::ios::binary),否则\n会被转成\r\n,破坏 CSV 结构
实际跑通的关键就两步:用 cmark 正确拿到表格 AST,再严格按 CSV RFC 4180 处理字段边界。中间任何一环跳过类型检查或假设格式干净,都会在某个边缘 case 上崩掉。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











