FormatArc Markdown to HTML 转换器中,转义与未转义符号的渲染对比FormatArc Markdown to HTML 转换器中,转义与未转义符号的渲染对比
作者: FormatArc 编辑部发布日期: 2026-09-02更新日期: 2026-09-02

Markdown 转义字符一览:让特殊符号原样显示的方法(23 例实测)

* 开头的行变成了项目符号列表。写了 # 价格清单 却变成了页面标题。本该显示的 <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 转换器 里立刻确认。转换完全在浏览器内完成,粘贴的内容不会被上传到任何地方。

FormatArc Markdown to HTML 转换器中,转义与未转义符号的渲染对比FormatArc 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 &lt;version&gt; with 2.0

\<version\> 在 4 种实现中全部正常显示为带尖括号的文字。如果占位符是命令参数或代码的一部分,用代码 span(反引号)包裹更能传达"这是代码"的意图。HTML 字符引用 &lt;version&gt; 在 4 种实现中也全部保留为文字(remark 系列在输出时规范化为 &#x3C;,但画面上的显示效果相同)。这和后面表格单元格中管道符用 &#124; 的方法一样。

全文档的例外 — 表格单元格内的管道符(|)是特殊的

到这里为止的规则在文档任何位置都适用,但表格单元格内的管道符 | 是唯一的例外。表格不是 CommonMark 核心规范,而是 GFM 扩展规范在新标签页中打开,单元格内出现未转义的管道符就会在那里分割列。更糟糕的是,GFM 规范规定超出表头列数的单元格 "the excess is ignored"(超出部分被忽略),实测中 grep a | b 这个单元格在管道符处被劈开后,后半部分的 b 被静默丢弃了(3 种 GFM 实现一致)。从"数据被悄悄丢失"这个角度看,这个错误和其他格式错误在性质上不同。

单元格内的管道符可以用反斜杠转义为 \|,也可以用 HTML 字符引用 &#124;。实测中两种方法在 3 种 GFM 实现中全部正常显示为同一单元格内的管道符。表格本身的语法与对齐设置,可参考 Markdown 表格语法

| 命令 |
| --- |
| grep a \| b |
| grep a &#124; 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 即使在单词内部也会变成斜体。

表格单元格内的管道符怎么转义?

在管道符前加反斜杠写成 \|,或使用字符引用 &#124;。实测中两种方法在 3 种 GFM 实现中全部有效。

代码块里想显示反斜杠或星号怎么办?

什么都不用做。代码块和代码 span 内部转义处理不生效,*\ 原样写就原样显示。反过来写 \* 的话,反斜杠本身也会被显示出来。

总结

  • 让特殊符号原样显示的基本规则是:在符号前加一个反斜杠 \。对象是 32 种 ASCII 标点符号,字母数字不适用。
  • snake_case、#hashtag、没有 URL 接续的方括号不需要转义。过度转义只会让源码变得难读。
  • 代码块和代码 span 内部转义不生效。行尾的反斜杠会变成强制换行。
  • <> 是文字消失式的错误,表格单元格内的管道符是数据丢失式的错误。这两个要优先处理。
  • 中文输入法的全角模式下输入的反斜杠和星号不是 ASCII 字符,不触发格式也不被转义。写 Markdown 时保持半角模式。

写完的 Markdown 会怎么渲染,粘贴到 Markdown to HTML 转换器 里就能立刻确认。免费、无需注册、粘贴的内容不会离开浏览器。