HTML 实体编码 / 解码

在线转义与反转义 HTML 实体。把 < > & " ' 转成命名实体,可选对全部非 ASCII 字符编码,也支持把命名或数字引用解码回文本。

把文本转义成可安全嵌入 HTML 的形式,或把实体还原成字符。 在任意一侧输入即可按该方向转换。最小模式只转义真正影响正确性的五个字符;扩展模式还会编码所有非 ASCII 字符,以获得最大兼容性。

常用实体对照

字符 命名实体 数字引用
& &amp; &#38;
< &lt; &#60;
> &gt; &#62;
" &quot; &#34;
' &#39; &#39;
\u{00A0} &nbsp; &#160;
\u{00A9} &copy; &#169;
\u{20AC} &euro; &#8364;

HTML 实体,以及转义为什么重要

会破坏 HTML 的五个字符

HTML 解析器把 < 当作标签开始,把 & 当作实体引用开始。只要你的内容里出现这两者,解析器就不再把它当作纯文本。转义的做法是用一个引用替换该字符,解析器会把引用还原成原字符,而不赋予它任何结构含义。

HTML 中真正需要转义的只有五个字符:

  • &&amp; —— 必须最先替换,否则后面的转义会被二次转义
  • <&lt;
  • >&gt;
  • "&quot; —— 出现在双引号属性值里时必须转义
  • '&#39; —— 出现在单引号属性值里时必须转义

严格来说,元素文本中只需要处理 &<。另外三个只在文本可能落进属性值时才要紧,而转义那一刻你通常无法预知它最终会去哪里,所以五个全转义才是稳妥习惯。

命名、十进制与十六进制引用

同一个字符有三种写法。不换行空格可以写成 &nbsp;&#160;&#xA0;。命名引用可读性最好,但 HTML5 的名称表约有 2200 条,老解析器认识的远少于此,因此冷门名称存在移植风险。数字引用适用于任何 Unicode 码点,永远安全。HTML5 中所有命名引用都要求结尾分号;少数遗留名称如 &amp 出于向后兼容仍能在无分号时被解析,但依赖这一点迟早出事。

转义不等于消毒

转义是阻止跨站脚本的关键:如果含 <script> 的用户输入被转义成 &lt;script&gt;,浏览器只会显示字面文本,不会执行任何脚本。但转义必须发生在正确的上下文里。在 <script> 块内、style 属性内或 URL 中,HTML 转义都不够,这些位置各有自己的编码规则。而且转义只应在输出时执行一次。输入时转一次、输出时又转一次,得到的是 &amp;lt; 和一屏乱码。

什么时候需要扩展模式

任何现代规范都不要求转义非 ASCII 字符。UTF-8 文档可以直接包含 é,而且通常这才是更好的选择:更短、更易读、也更好搜索。扩展模式是为那些字节流必须穿过会破坏非 ASCII 的通道的场景准备的 —— 某些遗留邮件模板、老旧 CMS 字段、声明编码含糊的 XML 流水线,或者卡在 Latin-1 的构建环节。如果你能端到端控制编码,就留在最小模式。

不换行空格的陷阱

&nbsp; 是 U+00A0,与普通空格 U+0020 是不同的字符。它阻止换行,也阻止浏览器合并连续空白,所以所见即所得编辑器会大量产出它。因为外观完全一致,它会悄无声息地破坏字符串比较、某些引擎中用 \s 写的正则,以及 CSV 导入。如果某个值看起来没问题却死活匹配不上,先检查有没有混进不换行空格。

开源说明:使用纯字符串与码点运算实现,未使用第三方库。

常见问题

大于号一定要转义吗?
严格来说不用 —— 文本中单独的 > 没有歧义,能正常解析。转义它只是约定俗成,因为代价为零,还能避免生成的标记产生混淆,尤其是当输出后续会被比浏览器更严格的工具处理时。
为什么 & 必须先转义?
因为其他所有转义序列都以 & 开头。如果你先把 < 换成 &lt; 再去转义 &,下一轮会把那个 & 变成 &amp;,页面上就出现 &amp;lt; 了。
只靠转义就能防住 XSS 吗?
只在 HTML 文本和属性上下文中、并且在输出时执行才有效。注入到 JavaScript、CSS 或 URL 中的内容需要各自语言的转义规则。转义是其中一层防护,不是完整方案。
命名实体和数字引用该用哪个?
数字引用普遍受支持,适用于任何码点。命名实体对大家都认识的那几个更可读。实践中:五个核心字符用命名,其他罕见字符用数字。
带重音的字符需要编码吗?
在 UTF-8 文档里不需要,而现代网页内容基本都是 UTF-8,直接写就好。只有当流水线中某个环节不能可靠传递非 ASCII 字节时,才使用扩展模式。
&nbsp; 和普通空格有什么区别?
不换行空格是码点 U+00A0。看起来一样,但它阻止换行、且不会与相邻空白合并。在比较时它也是完全不同的字符,这让它成为许多隐蔽 bug 的来源。