CSV 与 Markdown 表格互转指南:GFM 管道表格语法与状态机解析

为什么需要 CSV 与 Markdown 表格互转

CSV 与 Markdown 表格是两种最常见的表格数据格式,但它们服务于不同场景:

  • CSV:数据存储与交换的标准格式,被 Excel、数据库客户端、BI 工具广泛支持
  • Markdown 表格:文档呈现格式,被 GitHub、GitLab、Notion、VS Code 等文档平台原生渲染

两者之间的转换需求随处可见:

  • 将数据库查询结果(CSV 导出)转为 README 文档中的表格
  • 从 Excel 复制数据粘贴到 GitHub Issue 或 Pull Request
  • 将 Markdown 文档中的表格数据提取为 CSV 供程序处理
  • 在 Confluence / Notion 等平台间迁移表格数据

配套工具:CSV 与 Markdown 表格互转工具

一、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

  1. 用状态机解析 CSV 为二维数组
  2. 第一行作为表头(可选)
  3. 生成 GFM 表格:表头行 + 分隔行(含对齐方式)+ 数据行
  4. 单元格中的 | 自动转义为 \|

4.2 Markdown → CSV

  1. 解析 GFM 表格为二维数组
  2. 识别分隔行中的对齐方式
  3. 还原 \||
  4. 用 CSV 序列化器输出(需要时引号包裹)

4.3 对称性保障

双向转换的对称性要求:CSV → Markdown → CSV 应还原原始数据(除格式差异外)。

关键点:

  • 管道符转义可逆|\||
  • 引号包裹按需生成:CSV 序列化时仅对含分隔符、引号、换行的字段引号包裹
  • 对齐信息丢失:CSV → Markdown 时用户指定对齐方式,Markdown → CSV 时对齐信息被丢弃(CSV 无对齐概念)

五、与 CSV/JSON 互转工具的互补关系

本站点的 CSV/JSON 互转工具 与本工具专注于不同方向:

维度CSV / JSON 互转CSV 与 Markdown 互转
转换方向CSV ↔ JSONCSV ↔ Markdown 表格
核心能力嵌套对象展平、类型推断列对齐、管道符转义
适用场景数据导入导出、API 对接文档编写、README 表格
数据结构结构化数据(对象数组)扁平表格(二维数组)

两者形成完整的 CSV 工具链:

  1. 从数据库导出 CSV
  2. 用 CSV/JSON 互转做数据清洗(类型推断、嵌套展平)
  3. 用 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 表格互转的核心技术点:

  1. CSV 解析状态机:四状态逐字符解析,正确处理引号包裹、引号转义、字段内换行
  2. GFM 表格语法:管道符分隔、分隔行对齐、管道符转义
  3. 双向对称性:管道符转义可逆,CSV 序列化按需引号包裹
  4. 互补定位:与 CSV/JSON 互转工具形成完整 CSV 工具链

本工具纯原生 TypeScript 零依赖实现,所有转换在浏览器本地完成,适用于技术文档编写、README 表格生成、数据库导出转文档等场景。测试表格转换时需要大量样例数据,可配合测试数据批量生成器生成姓名、邮箱、URL、公司等 Mock 数据,填入 CSV 后转 Markdown 表格,快速生成结构化的测试文档。