为什么需要 CSV 与 Markdown 表格互转
CSV 与 Markdown 表格是两种最常见的表格数据格式,但它们服务于不同场景:
- CSV:数据存储与交换的标准格式,被 Excel、数据库客户端、BI 工具广泛支持
- Markdown 表格:文档呈现格式,被 GitHub、GitLab、Notion、VS Code 等文档平台原生渲染
两者之间的转换需求随处可见:
- 将数据库查询结果(CSV 导出)转为 README 文档中的表格
- 从 Excel 复制数据粘贴到 GitHub Issue 或 Pull Request
- 将 Markdown 文档中的表格数据提取为 CSV 供程序处理
- 在 Confluence / Notion 等平台间迁移表格数据
一、GFM Markdown 表格语法详解
1.1 基本结构
GFM(GitHub Flavored Markdown)表格使用管道符 | 分隔单元格,使用 --- 分隔表头与数据行:
| 姓名 | 年龄 | 城市 |
| --- | --- | --- |
| 张三 | 28 | 北京 |
| 李四 | 35 | 上海 |
首尾的 | 是可选的,但推荐保留以提高可读性。
1.2 列对齐方式
分隔行中的冒号 : 用于指定列对齐方式:
| 语法 | 对齐方式 | 适用场景 |
|---|---|---|
:--- | 左对齐 | 文本列(姓名、地址) |
:---: | 居中对齐 | 标题、短文本、状态标签 |
---: | 右对齐 | 数字列(金额、数量、百分比) |
--- | 默认 | 通常等同于左对齐 |
| 商品 | 单价 | 库存 |
| :--- | ---: | :---: |
| 键盘 | 199 | 50 |
| 鼠标 | 89 | 120 |
注意:对齐方式仅影响渲染显示,不影响数据内容。部分博客平台可能忽略冒号,但 GitHub、GitLab、VS Code 等主流平台均支持。
1.3 管道符转义
单元格内容中的 | 必须转义为 \|,否则会被误认为单元格边界:
| 表达式 | 结果 |
| --- | --- |
| a\|b | 匹配 a 或 b |
二、CSV 解析状态机
2.1 为什么需要状态机
CSV 看似简单(用逗号分隔字段),但实际解析需要处理多种边界情况:
- 字段内包含分隔符:
"北京,上海",28 - 字段内包含引号:
"他说""你好""",3 - 字段内包含换行:
"第一行\n第二行",5 - 空字段与尾部分隔符:
a,b,c,
用简单的 split(',') 无法正确处理这些情况,需要状态机逐字符解析。
2.2 四状态解析器
本工具实现了 RFC 4180 兼容的 CSV 解析状态机:
FIELD_START → UNQUOTED →(遇分隔符)→ FIELD_START
↓(遇 ")
QUOTED →(遇 ")→ QUOTE_MAY_END →(遇 ")→ QUOTED
→(遇分隔符)→ FIELD_START
FIELD_START(字段开始):
- 遇
"→ 进入QUOTED(引号包裹字段) - 遇分隔符 → 字段为空,继续等待下一字段
- 遇
\n→ 字段为空,行结束 - 其余字符 → 累积,进入
UNQUOTED
UNQUOTED(未引用字段):
- 遇分隔符 → 字段结束,回到
FIELD_START - 遇
\n→ 字段结束,行结束 - 其余字符 → 累积
QUOTED(引用字段):
- 遇
"→ 进入QUOTE_MAY_END(可能是结束引号) - 其余字符(含
\n)→ 累积
QUOTE_MAY_END(引用可能结束):
- 遇
"→ 转义为单个",回到QUOTED - 遇分隔符 → 引用结束,字段结束
- 遇
\n→ 引用结束,行结束 - 其余字符 → 容错处理,当作普通字符
2.3 关键实现细节
// 连续两个 " 转义为单个 "
case 'QUOTE_MAY_END':
if (ch === '"') {
currentField += '"';
state = 'QUOTED';
}
字段内换行是 CSV 的合法特性。状态机在 QUOTED 状态下不处理 \n 为行结束,而是累积到字段中,这对于多行地址、长描述等字段至关重要。
三、Markdown 表格解析
3.1 分隔行识别
Markdown 表格的第二行必须是分隔行,每个单元格仅含 - 和 : 字符。解析器通过正则 /^:?-+:?$/ 验证:
const isSeparatorCell = (cell: string): boolean =>
/^:?-+:?$/.test(cell.trim()) && cell.includes('-');
如果第二行不符合分隔行格式,解析器返回错误,避免将普通文本误判为表格。
3.2 转义管道符的还原
解析 Markdown 表格时,需要区分单元格分隔符 | 与被转义的字面管道符 \|:
// 按未转义的 | 分割
if (ch === '\\' && inner[i + 1] === '|') {
current += '\\|'; // 保留转义序列
i++;
} else if (ch === '|') {
cells.push(current.trim()); // 真正的分隔符
current = '';
}
分割后再将 \| 还原为 |,确保数据完整性。
四、双向转换的对称性
4.1 CSV → Markdown
- 用状态机解析 CSV 为二维数组
- 第一行作为表头(可选)
- 生成 GFM 表格:表头行 + 分隔行(含对齐方式)+ 数据行
- 单元格中的
|自动转义为\|
4.2 Markdown → CSV
- 解析 GFM 表格为二维数组
- 识别分隔行中的对齐方式
- 还原
\|为| - 用 CSV 序列化器输出(需要时引号包裹)
4.3 对称性保障
双向转换的对称性要求:CSV → Markdown → CSV 应还原原始数据(除格式差异外)。
关键点:
- 管道符转义可逆:
|→\|→| - 引号包裹按需生成:CSV 序列化时仅对含分隔符、引号、换行的字段引号包裹
- 对齐信息丢失:CSV → Markdown 时用户指定对齐方式,Markdown → CSV 时对齐信息被丢弃(CSV 无对齐概念)
五、与 CSV/JSON 互转工具的互补关系
本站点的 CSV/JSON 互转工具 与本工具专注于不同方向:
| 维度 | CSV / JSON 互转 | CSV 与 Markdown 互转 |
|---|---|---|
| 转换方向 | CSV ↔ JSON | CSV ↔ Markdown 表格 |
| 核心能力 | 嵌套对象展平、类型推断 | 列对齐、管道符转义 |
| 适用场景 | 数据导入导出、API 对接 | 文档编写、README 表格 |
| 数据结构 | 结构化数据(对象数组) | 扁平表格(二维数组) |
两者形成完整的 CSV 工具链:
- 从数据库导出 CSV
- 用 CSV/JSON 互转做数据清洗(类型推断、嵌套展平)
- 用 CSV 与 Markdown 互转将清洗后的数据写入文档
六、典型应用场景
6.1 GitHub README 表格
将 Excel 数据直接粘贴为 Markdown 表格,避免手动添加管道符:
Excel 数据 → 复制为 CSV → 本工具转为 Markdown → 粘贴到 README
6.2 数据库导出转文档
SQL 客户端(如 DBeaver、DataGrip)导出查询结果为 CSV,转为 Markdown 表格写入技术文档:
SELECT name, email, role FROM users LIMIT 10;
-- 导出 CSV → 转 Markdown → 粘贴到 API 文档
6.3 Markdown 表格数据提取
从 Markdown 文档中提取表格数据转为 CSV,在 Excel 中排序、筛选、计算后再转回:
Markdown 表格 → 本工具转为 CSV → Excel 处理 → 转回 Markdown
6.4 文档迁移
在不同平台间迁移表格数据时,CSV 作为中间格式最通用:
Confluence 表格 → 导出 CSV → 转 Markdown → 粘贴到 GitHub Wiki
总结
CSV 与 Markdown 表格互转的核心技术点:
- CSV 解析状态机:四状态逐字符解析,正确处理引号包裹、引号转义、字段内换行
- GFM 表格语法:管道符分隔、分隔行对齐、管道符转义
- 双向对称性:管道符转义可逆,CSV 序列化按需引号包裹
- 互补定位:与 CSV/JSON 互转工具形成完整 CSV 工具链
本工具纯原生 TypeScript 零依赖实现,所有转换在浏览器本地完成,适用于技术文档编写、README 表格生成、数据库导出转文档等场景。测试表格转换时需要大量样例数据,可配合测试数据批量生成器生成姓名、邮箱、URL、公司等 Mock 数据,填入 CSV 后转 Markdown 表格,快速生成结构化的测试文档。