学完这篇你能分清 HTML 里哪些字符必须转义、哪些写法看着对其实会出错,以及 、&、编码乱码这几个高频坑怎么绕开。
先记住一张「必转义字符表」
在 HTML 正文里,只有 5 个字符有硬性转义要求,其余都是可读性选择:
| 字符 | 实体 | 说明 |
|---|
& | & | 必须,否则会吞掉后面的实体写法 |
< | < | 必须,否则被当成标签开头 |
> | > | 建议,单独出现时浏览器多数能容错 |
" | " | 属性值用双引号包裹时必须 |
' | ' | 属性值用单引号包裹时必须 |
注意:只有这 5 个。很多人顺手把中文、字母、括号也转成实体,结果是文件变大、源码没法读,而且搜索和替换会变得极其痛苦。没必要。
第一步:搞清三种写法,别混着用
同一个字符有三种合法写法:
& <!-- 命名实体 -->
& <!-- 十进制数字实体 -->
& <!-- 十六进制数字实体 -->
命名实体好记但数量有限(HTML5 约 2000 个),数字实体万能但需要查码点。日常优先用命名实体,遇到冷门符号再用数字实体。
注意:分号不能省。& 这种缺分号的写法在 HTML 里靠浏览器容错,进了 XML、SVG、RSS 或邮件模板就直接报解析错误。写全 &。
第二步:转义顺序——& 永远第一个换
这是最经典的坑。假设你要展示一行代码 <,如果先转 < 再转 &:
原文: <
转 < 后: <
再转 & 后: &lt; ← 错了,页面上会显示 < 而不是 <
正确顺序永远是:先 & → 再 < > → 最后 " '。
如果你用后端函数(PHP 的 htmlspecialchars、Python 的 html.escape、JS 里自己写的工具函数),确认它内部就是这个顺序,别自己再套一层。
第三步:属性值里的引号,是个概率游戏
<!-- 危险:值里有双引号会直接截断 -->
<input title="他说"你好"">
<!-- 正确 -->
<input title="他说"你好"">
注意:只要属性值里可能出现引号,就统一把引号转义成实体,或者干脆给这个值外面套另一种引号。别赌数据里没有引号——用户输入的昵称、签名、标题里什么都有。
第四步: 不是空格,别拿它排版
是不换行空格(U+00A0),它的特点是:不折叠、不换行、宽度固定。所以:
- 拿它做缩进 → 手机上文字被撑出屏幕、出现横向滚动条
- 拿它做间距 → 复制粘贴出去全是奇怪的空白字符
- 屏幕阅读器读它 → 会读出「空格空格空格」
正确做法:缩进用 text-indent: 2em,间距用 margin/padding,段间距用 margin-bottom。
真要用 的场景只有两个:防止某个短词组被拆行(比如「30 kg」「第 3 页」)、以及空单元格占位。
第五步:URL 里的 & 和 HTML 里的 & 要分开看
这一条极容易搞混:
URL 本身: a.html?x=1&y=2
写进 href 时: <a href="a.html?x=1&y=2">
URL 语法里 & 就是分隔参数用的,不需要转义;但这段 URL 出现在 HTML 属性里时,就要按 HTML 规则转成 &。浏览器读出属性值后会还原成 & 再请求,所以服务器端收到的还是 &。
注意:如果你用 JS 的 URLSearchParams 或后端函数生成 URL,别忘了放进模板前再过一遍 HTML 转义。反过来的错误也常见——在 URL 编码函数里把 & 转成 &,服务器就收到一个不存在的参数名 amp;y。
第六步:JS 里拼 HTML,别用 innerHTML
// 危险:用户输入直接进 DOM
box.innerHTML = '<div>' + userInput + '</div>';
// 安全:要文本用 textContent
box.textContent = userInput;
确实需要拼 HTML 时,先转义再拼。另外还有个隐蔽坑:内联 <script> 里出现的字符串 </script> 会提前关闭脚本块,必须写成 <\/script>。
第七步:编码不一致,转义写得再对也是乱码
页面上出现「锟斤拷」「é」「�」这类东西,通常不是实体写错,而是:
- 页面没有声明
<meta charset="utf-8">
- 文件本身存成了 GBK,却按 UTF-8 输出
- 数据库连接不是
utf8mb4,Emoji(4 字节)直接存坏
注意:MySQL 的 utf8 只支持 3 字节,只写 utf8 会丢 Emoji,必须用 utf8mb4。这一条踩过的人最多。
第八步:常用符号速查
排版类:—(—)、–(–)、…(…)、« »(« »)、·(·)、•(•)
版权类:©(©)、®(®)、™(™)
数学类:×(×)、÷(÷)、±(±)、°(°)、≠(≠)、≤ ≥(≤ ≥)、∞(∞)
箭头类:→(→)、←(←)、↔(↔)
Emoji 这类超出基本平面的字符,用数字实体写:😀(😀)。
小结
- 正文只需转义
& < > " ' 五个字符,其余别多转
- 转义顺序固定:
& 排第一,否则会产生 &lt; 这种双重转义
- 实体末尾的分号不能省,XML 和邮件模板不给你容错
是排版毒药,布局问题交给 CSS
- URL 里的
& 在写进 HTML 属性时才转义,两个层面别搞混
- 乱码优先怀疑编码声明和数据库字符集,而不是转义写法
- 拼接 HTML 用
innerHTML 前先转义,能不用就不用