以 * 开头的行变成了项目符号列表。写了 # 价格清单 却变成了页面标题。本该显示的 <version> 占位符在预览里消失了。这些都没有报错,只是 Markdown 把符号当成了"格式指令"来解析。这篇文章介绍如何转义符号使其原样显示,并附上 4 种渲染器 23 例实测结果。
先说结论 — 在符号前面加一个反斜杠(\)
想在结果里显示的特殊符号前面写一个反斜杠 \,仅此而已。
\*这句话不会变成斜体\*
反斜杠本身不会出现在结果中。上面这个例子,包括首尾的星号在内,会原样显示为字符串 *这句话不会变成斜体*。
如果想显示反斜杠本身,写两个 \\ 即可。
按症状反查的速查表
从"出了什么问题"反查对应的特殊符号。以下全部来自后文的实测。
| 出现的现象 | 原因 | 解决方法 |
|---|---|---|
| 变成斜体或加粗 | 用 * 或 _ 包住了词语 | 前面加 \*、\_ |
| 整行变成标题 | 行首是 # + 空格 | 写成 \# |
| 变成编号列表 | 行首是"数字 + 句点" | 句点前加 \,如 1986\. |
| 变成项目符号列表 | 行首是 - 或 * | 写成 \-、\* |
| 文字从页面上消失 | 用 < 和 > 包住的词 | 写成 \< 和 \> |
| 表格单元格被劈开或后半部分消失 | 单元格内的管道符(|) | 管道符前加 \(详见后文) |
可转义的特殊符号列表(32 种)
CommonMark 规范中的 Backslash escapes在新标签页中打开 规定:"Any ASCII punctuation character may be backslash-escaped"(所有 ASCII 标点符号都可以用反斜杠转义)。对象是以下 32 个字符:
! " # $ % & ' ( ) * + , - . / : ; < = > ? @ [ \ ] ^ _ ` { | } ~
这条规则背后有两个"反面规则",很多错误正是从这里产生的:
- 对象仅限 ASCII 特殊符号。字母数字前的反斜杠只是普通字符,原样保留。规范明确写道 "Backslashes before other characters are treated as literal backslashes",写
\n不会变成换行,而是输出反斜杠和字母 n。 - 全角符号(如 ※、「」等)本身不会触发 Markdown 格式,因此不需要转义。在前面加反斜杠的话,反斜杠本身反而会显示出来。
还有一点很重要:是否需要转义不只取决于字符本身,还取决于"出现位置"。# 只有在行首且后面跟空格时才会变成标题,句子中间的 # 原样写就很安全。下一节的实测量化了这个边界。
实测 — 不转义会发生什么,反斜杠是否真的有效
讲 Markdown 转义的文章很多,但把"不转义时实际会发生什么"和"反斜杠在各实现中是否真的生效"都附上验证结果的资料很少。因此我们用 23 个测试用例跑了 4 种实现:GitHub 生产渲染器(通过 Markdown API)、marked 18.0.5、remark-gfm 4.0.1、无扩展的严格 CommonMark(remark-parse 单体)。测量日期为 2026-08-26,复现脚本和原始数据在仓库的 scripts/benchmarks/markdown-escape-characters/ 目录下。
主要用例的结果如下。
| 输入(实测字符串) | 不转义的结果 | 转义后的结果 |
|---|---|---|
Buy the *limited edition* today. | 变成斜体(4 种实现一致) | \*limited edition\* 原样显示(4 种一致) |
行首的 # price list | 变成 h1 标题(4 种实现一致) | \# price list 原样显示(4 种一致) |
行首的 1986. What a year. | 变成从 1986 开始的编号列表(4 种实现一致) | 1986\. What a year. 原样显示(4 种一致) |
Replace <version> with 2.0 | <version> 从页面上消失(4 种实现一致) | \<version\> 正常显示(4 种一致) |
表格单元格内的 grep a | b | 管道符处单元格被劈开,超出表头列数的部分被丢弃(3 种 GFM 实现一致) | 反斜杠前置后保持在同一单元格内(3 种 GFM 一致) |
"数字 + 句点"的列表化是最容易忽略的错误。不管是 1. 还是 1986.,只要出现在行首就会被解释为编号列表,后面的文字会被缩进。如果用年份或型号开头,把句点转义为 \. 即可。
你写的 Markdown 会被渲染器如何解析,可以粘贴到 Markdown to HTML 转换器 里立刻确认。转换完全在浏览器内完成,粘贴的内容不会被上传到任何地方。


这 4 种实现都属于 CommonMark 体系,转义的基本行为完全一致。实现之间的方言差异在哪里体现,可参考 CommonMark 与 GFM 的区别。
全角与半角符号的混淆 — 中文输入特有的陷阱
上面 32 个可转义字符全部是 ASCII 半角字符。但中文输入法有一个容易被忽略的特性:全角/半角切换。
中文输入法(搜狗、百度、macOS 自带拼音等)默认是"全角"模式,按 Z 或 Shift 可以切换到半角。在全角模式下:
- 按
*键输入的是全角星号*(U+FF0A),不是半角的*(U+002A) - 按
\键输入的是全角反斜杠\(U+FF3C),不是半角的\(U+005C) - 按
#键输入的是全角井号#(U+FF03),不是半角的#(U+0023)
这带来两个后果:
- 全角符号不会触发 Markdown 格式,因此不需要转义。
# 价格清单不会变成标题,因为#不是#。同理*不会触发斜体。 - 全角反斜杠不是转义字符。
\*中的\对渲染器来说只是一个普通的全角字符,不会让后面的*失去格式含义。如果你想转义星号,必须确保输入的是半角\。
实际工作中常见的情况是:你在半角模式下写了一行 Markdown,切到全角模式输入中文时忘了切回来,结果写出来的"反斜杠"其实是全角的。渲染结果里多了一个不易察觉的 \ 字符,格式还是该出错的出错。
排查方法:把可疑符号复制出来,用任意 Unicode 查询工具查看码点。如果是 U+FF 段(FF00-FFEF),就是全角字符。写 Markdown 时建议全程保持半角模式,中文由输入法自动处理,标点符号用 ASCII 半角输入。
其实不需要转义的几种情况
过度转义不仅降低源码可读性,还会在 diff 审查中产生噪音。实测确认"不需要转义"的典型场景有 3 种:
- 单词内部的下划线:
max_retry_count_limit这样的 snake_case 在 4 种实现中全部原样显示。单词中间的_不会被解释为强调。 - 没有空格的
#hashtag:#变成标题的条件是后面跟空格,#hashtag在 4 种实现中全部保持为普通字符串。 - 没有 URL 接续的方括号:
[TODO] 稍后修改这种写法在 4 种实现中全部原样显示。但注意,如果文档内某处存在[TODO]: https://...形式的引用链接定义,方括号就会变成链接——只有这种情况才需要写成\[TODO\]。
需要注意的一点:星号和下划线的行为不对称。foo*bar*baz 中单词内部的星号在 4 种实现中全部变成了斜体。如果凭 snake_case 的习惯不管星号,格式就会出问题。
反斜杠不生效的地方
CommonMark 规范明确规定:"Backslash escapes do not work in code blocks, code spans, autolinks, or raw HTML"(反斜杠转义在代码块、代码 span、自动链接和原始 HTML 内部不生效)。实测也证实,代码 span 和代码块内写的 \* 在 4 种实现中全部连反斜杠一起输出了。
这在实务中有两层含义:
- 代码块内部不需要转义任何东西。
*和#原样写就原样显示。 - 在代码里试图转义反而会把事情搞糟。写
`\*`的话,读者看到的是\*而不是*。
还有一个行尾特有的陷阱。规范规定 "A backslash at the end of the line is a hard line break"(行尾的反斜杠是强制换行),实测中 4 种实现全部把行尾的 \ 转换成了 <br>。如果你想转义行尾的符号,结果把反斜杠放在了最后一个位置,产生的不是符号而是换行。
消失的字符 — 尖括号 < > 要特别注意
其他符号的问题不过是"被加了不该有的格式",但 < 和 > 是文字整个消失。如果你有写 <version> 这种占位符的习惯,这就是高频事故。
实测中,Replace <version> with 2.0 这行里的 <version> 在 4 种实现中全部从页面上消失了。消失的机制各实现不同:GitHub 的净化器(sanitizer)移除了未识别的标签,marked 和 remark 则把它作为原始 HTML 标签输出,浏览器把它当作未定义标签吞掉了。无论哪条路径,读者都看不到。
解决方法有三种:
Replace \<version\> with 2.0
Replace `<version>` with 2.0
Replace <version> with 2.0
\<version\> 在 4 种实现中全部正常显示为带尖括号的文字。如果占位符是命令参数或代码的一部分,用代码 span(反引号)包裹更能传达"这是代码"的意图。HTML 字符引用 <version> 在 4 种实现中也全部保留为文字(remark 系列在输出时规范化为 <,但画面上的显示效果相同)。这和后面表格单元格中管道符用 | 的方法一样。
全文档的例外 — 表格单元格内的管道符(|)是特殊的
到这里为止的规则在文档任何位置都适用,但表格单元格内的管道符 | 是唯一的例外。表格不是 CommonMark 核心规范,而是 GFM 扩展规范在新标签页中打开,单元格内出现未转义的管道符就会在那里分割列。更糟糕的是,GFM 规范规定超出表头列数的单元格 "the excess is ignored"(超出部分被忽略),实测中 grep a | b 这个单元格在管道符处被劈开后,后半部分的 b 被静默丢弃了(3 种 GFM 实现一致)。从"数据被悄悄丢失"这个角度看,这个错误和其他格式错误在性质上不同。
单元格内的管道符可以用反斜杠转义为 \|,也可以用 HTML 字符引用 |。实测中两种方法在 3 种 GFM 实现中全部正常显示为同一单元格内的管道符。表格本身的语法与对齐设置,可参考 Markdown 表格语法。
| 命令 |
| --- |
| grep a \| b |
| grep a | b |
如果表格数据来自 CSV 或电子表格,用 CSV to Markdown 转换器 可以自动生成已转义管道符的表格。不需要逐个单元格目视检查,转换在浏览器内完成。
如果想以代码形式展示,用代码 span 包裹
除了反斜杠,用代码 span(反引号包裹)也能让特殊符号原样显示。但两者的用途有明确区分:
- 想在句子中展示某个符号时用反斜杠:写
价格是 \*随时可能\* 变动的,保持正文的自然排版,只是符号原样显示。 - 想展示代码、命令、文件路径、正则表达式时用代码 span:
C:\Users\name或\d+这样的字符串用等宽字体显示,读者能明确看出这是代码。
代码 span 内部转义处理本身不生效,所以正则表达式的 \d+ 可以原样写,这是一个很大的便利。把现有的 HTML 页面或富文本转换成 Markdown 时的转义处理,可参考 HTML 转 Markdown 指南。
写入的平台不同,反斜杠可能不生效
前面的实测全部针对 Markdown 渲染器的 4 种实现。但实际写文章的目的地经常是 Slack 或 Notion 这样的协作工具,这些和渲染器的问题要分开对待。以下是各服务官方文档中确认的范围:
| 写入平台 | 是否支持 \ 转义 | 确认依据 |
|---|---|---|
GitHub(.md / Issue / PR) | 支持 | 本文实测(GitHub Markdown API)及 GFM 规范在新标签页中打开 |
| GitLab | 支持 | GitLab 官方文档在新标签页中打开 列出了 31 种保留 ASCII 字符 |
| Obsidian | 支持 | Obsidian 官方帮助在新标签页中打开 明确列出 \*、\_、\#、\`、|、\~ |
| Slack | 不支持 | Slack 官方文档在新标签页中打开 只规定了 &、<、> 的 HTML 实体,未提及反斜杠 |
| Notion | 官方文档未提及 | Notion 官方帮助在新标签页中打开 说明了 **、*、`、~ 的快捷键,但没有转义条目 |
由此可以得出两点:
- GitHub、GitLab、Obsidian 的行为与其渲染器实现一致,且有官方文档背书。上面实测表直接适用。
- Slack 需要换一种方法。Slack 的转义规则只覆盖
&、<、>的 HTML 实体,没有文档化的方法用反斜杠取消*或_。如果想确保符号原样显示,用反引号做成代码 span 是实务上的替代方案。
Notion 的官方帮助没有关于转义的记载,所以本表没有写"支持"或"不支持"。文档未记载的行为如果凭一次实测就下结论,规格变更时就会变成错误信息。Discord 也在候选中,但因无法查阅其官方文档,未纳入本表。
常见问题
Markdown 中哪些特殊符号可以转义?
ASCII 标点符号全部 32 种(CommonMark 规范在新标签页中打开)。字母数字或全角符号前面的反斜杠不是转义,反斜杠本身会作为字符保留。
写了 \n 却没有换行
\n 是编程语言中的换行记法,不是 Markdown 的转义。反斜杠 + 字母会原样输出(4 种实现实测一致)。在 Markdown 中换行的方法是:空一行开始新段落,或在行尾放反斜杠做强制换行。
snake_case 的下划线需要转义吗?
不需要。单词内部的 _ 在 4 种实现中全部没有被解释为强调。但星号是例外,foo*bar*baz 即使在单词内部也会变成斜体。
表格单元格内的管道符怎么转义?
在管道符前加反斜杠写成 \|,或使用字符引用 |。实测中两种方法在 3 种 GFM 实现中全部有效。
代码块里想显示反斜杠或星号怎么办?
什么都不用做。代码块和代码 span 内部转义处理不生效,* 和 \ 原样写就原样显示。反过来写 \* 的话,反斜杠本身也会被显示出来。
总结
- 让特殊符号原样显示的基本规则是:在符号前加一个反斜杠
\。对象是 32 种 ASCII 标点符号,字母数字不适用。 - snake_case、
#hashtag、没有 URL 接续的方括号不需要转义。过度转义只会让源码变得难读。 - 代码块和代码 span 内部转义不生效。行尾的反斜杠会变成强制换行。
<和>是文字消失式的错误,表格单元格内的管道符是数据丢失式的错误。这两个要优先处理。- 中文输入法的全角模式下输入的反斜杠和星号不是 ASCII 字符,不触发格式也不被转义。写 Markdown 时保持半角模式。
写完的 Markdown 会怎么渲染,粘贴到 Markdown to HTML 转换器 里就能立刻确认。免费、无需注册、粘贴的内容不会离开浏览器。