因为真不需要——只需处理bold、italic、code等基础格式,引入复杂库是冗余;手动解析用strings.index扫描、strings.builder拼接,按\n\n分段再处理换行,性能更高且避免正则错误。

为什么不用现成的 Markdown 库,而要手写简单解析器
因为真不需要——如果你只处理 **bold**、*italic*、`code`、换行和段落,引入 blackfriday 或 goldmark 就是带火箭打蚊子。它们的 AST 构建、扩展点、HTML 转义逻辑,在纯文本转富文本预览场景里全是冗余开销。
常见错误现象:strings.ReplaceAll 连续套用导致嵌套标记错乱(比如 **a *b* c** 被拆成两段);正则全局替换没考虑边界,把 URL 里的 * 也当斜体了。
- 优先用
strings.Index+strings.IndexByte手动扫描,避免正则回溯爆炸 - 标记必须成对出现且不交叉,所以每次找到起始符后,要向后找「最远的有效结束符」,而不是第一个
- 原始字符串要原样保留未匹配部分,别用
Replace覆盖式修改
如何安全提取并转换 **text** 和 *text*
加粗和斜体共享同一套扫描逻辑,但触发条件不同:双星号要求前后都是非单词字符或边界,单星号还要排除它出现在单词中间(如 foo*bar 不应被识别)。
使用场景:用户输入的轻量级评论、API 返回的提示文案、CLI 工具的 help 输出渲染。
立即学习“go语言免费学习笔记(深入)”;
本文档主要讲述的是Android数据格式解析对象JSON用法;JSON可以将Java对象转成json格式的字符串,可以将json字符串转换成Java。比XML更轻量级,Json使用起来比较轻便和简单。JSON数据格式,在Android中被广泛运用于客户端和服务器通信,在网络数据传输与解析时非常方便。希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
- 用
strings.FieldsFunc按空白切分再逐段处理,比整串扫描更易控制上下文 - 检测
**时,检查前一个 rune 是否为0(开头)或空格/标点,后一个 rune 同理 - 斜体的
*若紧邻字母(如a*b),跳过;但a *b* c中的*b*是合法的 - 性能影响:纯遍历
[]byte比正则快 3–5 倍,内存分配少 90%(无regexp.Compile编译开销)
strings.Builder 在拼接过程中的关键作用
手动解析时,输出不是一次性生成的,而是边扫描边追加 HTML 片段。用 + 拼接字符串会频繁分配内存,strings.Builder 把这事压到最低开销。
容易踩的坑:Builder.String() 返回的是拷贝,如果后续还要修改 builder,得记得 Reset();更隐蔽的是,builder 底层 cap 不足时扩容策略不如预期,初始 Grow(128) 能省掉多数 realloc。
- 声明时直接指定预估长度:
var b strings.Builder; b.Grow(len(src) * 2) - 插入 HTML 标签用
b.WriteString("<strong>")</strong>,别用fmt.Fprintf—— 它会触发格式化开销 - 遇到未识别内容,直接
b.WriteString(s[start:i]),不转义;需要转义时单独调用html.EscapeString
换行与段落怎么算“简单”又不出错
Markdown 的段落是靠空行分隔,不是靠 \n。很多手写解析器一看到 \n 就插 <br>,结果把一段文字硬切成多行,语义全毁。
正确做法:先按 \n\n 切出段落块,每个块内再把连续 \n 替换为 <br>,但仅限于非空段落内部。
- 用
strings.Split分两次:strings.Split(input, "\n\n")得段落,再对每段strings.ReplaceAll(p, "\n", "<br>") - 注意 Windows 行尾
\r\n:统一用strings.ReplaceAll(input, "\r\n", "\n")预处理 - 兼容性影响:不处理
\r会导致 macOS/iOS 粘贴进来的文本段落合并
最常被忽略的一点:所有标记解析都必须在段落切分之后做,否则 \n 会干扰星号配对判断——比如 **a\nb** 不应被识别为加粗。









