用blackfriday v2提取Markdown标题最稳妥,需New()+Parse()+遍历ast.Document获取ast.Heading节点,Level为1~6,递归FirstChild提取纯文本;goldmark更现代但需手动展平子节点且注意BOM问题。

Go语言中用blackfriday解析Markdown并提取#标题
直接用blackfriday(v2)是最稳妥的选择,它能正确处理嵌套、转义和各种边界情况。别自己正则匹配^#\s+——遇到### `code` in heading或缩进列表里的# not a heading就挂了。
实操要点:
-
blackfriday.Run()返回HTML,但你需要原始AST节点,所以得用blackfriday.New()+Parse()+ 遍历*ast.Document - 只关心
*ast.Heading节点,其Level字段就是1~6,FirstChild往下递归取纯文本(跳过*ast.Code、*ast.Softline等) - v2默认不启用
EXTENSION_AUTO_HEADER_IDS,不用管ID生成逻辑,专注提取内容即可
用goldmark提取标题更现代但需手动遍历AST
goldmark是当前Go生态推荐的Markdown库,但它的AST设计更严格:标题节点是*ast.Heading,但文本内容藏在Heading.Destination(仅限自动ID)或需遍历Heading.FirstChild子树。
容易踩的坑:
立即学习“go语言免费学习笔记(深入)”;
将 Markdown 格式的回复转换为适合 Telegram 的富文本,避免渲染错误。适用于用户偏好 Markdown 但目标平台为 Telegram 的场景。
- 默认解析器不开启
WithHeadingAttribute()扩展,Destination为空,必须手动展平所有Text、String、Code子节点 -
goldmark.TextReader读取文件时若含BOM,会导致首行标题漏解析,建议先用bytes.TrimPrefix(b, []byte("\xef\xbb\xbf")) - 别依赖
ast.String()——它会把**bold**渲染成**bold**,不是纯文本
简单场景下用正则提取标题的适用边界
如果确定Markdown文件格式极简(无代码块、无引用块、无缩进、无转义#),可用regexp.MustCompile(`^#{1,6}\s+(.+)$`)配合FindAllStringSubmatch。
但必须加限制:
- 逐行扫描,且每行
strings.TrimSpace后才匹配,否则空行或缩进行干扰结果 - 跳过
```代码块内部——需维护一个inFence布尔状态,遇到^```.*$翻转 - 匹配到的
$1仍要strings.TrimSpace,因为## a应提取为a - 完全无法处理
## Heading with [link](/),链接文字会被当标题一部分
从Go源码文件里过滤出//go:generate或//lint:ignore这类标记行
这不是Markdown处理,但常和文档提取混用。关键不是“注释”,而是识别特定前缀的行级指令。
操作建议:
- 用
bufio.Scanner逐行读,避免strings.Contains误伤字符串字面量里的//go:generate - 检查每行是否以
//go:generate开头(注意是//后紧跟,不能有空格),再strings.Fields切分取第二字段作为命令 -
//lint:ignore后面通常跟SA1019这类规则名,用strings.SplitN(line, " ", 3)更安全,避免空格分割错误 - 别用
go/parser——它解析语法树,但这些是伪指令,不在AST中
真正麻烦的是混合场景:一个.md里插了```go代码块,代码块里又有//go:generate;此时标题提取和指令过滤必须分层隔离,先按代码块切片,再分别处理非代码区域和代码区域——这个分层逻辑没人替你做。

















