HTML 实体编解码
特殊字符与 & < 等 HTML 实体互相转换
HTML 实体编解码:什么时候必须转义,什么时候不必
编码与解码在做什么
编码把五个有语法意义的字符替换成实体。这五个字符就是"必须转义"的全部:
| 字符 | 实体 | 为什么必须转义 |
|---|---|---|
& | & | 实体的起始字符,不转义会让后面的序列被误解析 |
< | < | 标签开始,不转义会破坏文档结构 |
> | > | 标签结束(单独出现时多数场景可省,但建议一起转) |
" | " | 属性值定界符 |
' | ' | 属性值也可能用单引号定界 |
解码是反方向:把实体形式还原成字符。除了上面这五个,还支持命名实体(如 © —)和数字实体(十进制 ©、十六进制 ©,都表示版权符号)。工具的解码借助浏览器自身的解析引擎,所以对实体的支持范围与浏览器一致——这比自己维护一张实体表可靠。
什么时候必须转义
判断标准只有一条:这段文本会被放进 HTML 里当内容或属性值吗?
- 把用户输入显示到页面上——必须转义,否则就是 XSS 漏洞。用户提交
<script>alert(1)</script>,不转义就会被浏览器当成脚本执行。 - 把数据拼进 HTML 字符串(服务端模板拼接、前端字符串拼接)——必须转义。用户填的商品名里有一个
&就能让页面结构错乱。 - 把代码片段作为文本展示——必须转义。
<div>想显示成文字而不是被渲染成元素,就得写成<div>(很多文档站的手工做法就是这个)。
什么时候不必转义——以及为什么仍常被误用
| 场景 | 是否需要 | 说明 |
|---|---|---|
| JSON 字符串内部 | 不需要 | JSON 有自己的转义规则(\" \\),用 HTML 实体属于类型错误 |
| URL 参数 | 不需要 | 用百分号编码(%26),不是 & |
| 纯文本文件、日志 | 不需要 | 没有 HTML 解析,转义只会让内容不可读 |
| 数据库存储 | 不需要 | 存原文,输出时按目标格式转义。这很重要——见下 |
| CSV 单元格 | 不需要 | CSV 有自己的引号规则 |
第四行是重点:不要"在入库时转义"。这个做法看起来安全,实际会造成三种麻烦:
- 双重转义。入库时转一次、输出时再转一次,用户看到的是
&lt;这种原始实体。 - 数据不可逆。存的是
&,你就再也无法还原用户原本输入的是&还是&——两个不同的输入存成了同一个值。 - 非 HTML 用途被污染。同一份数据要导出成 CSV、发到 API、写进邮件,全部带着 HTML 实体。
正确原则是"存原文,按输出场景转义"。这也是现代模板引擎的默认行为(自动转义),所以用框架时通常什么都不用做——反而手动"加一层转义"是常见的 bug 来源。
常见症状对照
| 你看到的 | 原因 | 修法 |
|---|---|---|
<div> 显示在页面上 | 转义了两次,或多转了一层 | 找到重复转义的那一步,去掉一处 |
页面显示 & | 存的是 &,或入库时转义过 | 清洗存量数据,改掉入库转义的做法 |
& 出现在 JSON 里 | 用 HTML 实体代替了 JSON 转义 | 改用 JSON 转义工具 |
复制的内容带 | 从网页复制时带出了实体 | 解码一次;注意 (U+00A0)与普通空格不是同一个字符,去重和比较时可能出问题 |
最后一行的细节值得记住:不换行空格(U+00A0)不是普通空格。它看起来一样,但在字符串比较、正则 \s(在部分实现里不匹配它)、以及粘贴进代码时都会造成"看起来没问题但就是不对"的故障。用空白处理工具统一替换成普通空格是常见做法。
工具怎么用
编码按钮处理上面列的五个必须转义字符;解码按钮还原上述五类实体(含命名与数字形式)。全程在浏览器本地完成。
常见问题
编码会处理所有字符吗?
会转义 & < > " ' 五个在 HTML 中具有特殊含义的字符,其余字符原样保留,避免破坏显示。
解码支持数字实体吗?
支持。解码借助浏览器解析,{ 等十进制、十六进制实体以及命名实体都能正确还原。