十六进制数字实体的双向转换
HTML 数字实体的十六进制形式是 &#x 加码点十六进制再加分号,例如中文「中」写作 中。它本质上是把码点用十六进制写出来,因此正反向转换都可以按码点运算完成,不需要实体表。同一份内容通常还混着命名实体与十进制实体,转换工具应能同时处理三种形式。
基本信息
| 十六进制形式 | 中 前缀 &#x,后缀分号,十六进制数字大小写均可 |
|---|---|
| 十进制形式 | 中,两种形式的语义完全相同 |
| 位数 | 不强制固定位数,惯例补足 4 位,BMP 以上补到 5 到 6 位 |
| 范围 | 有效码点为 U+0000 到 U+10FFFF,其余按规范替换 |
| 解析时机 | 由 HTML 解析器在词法阶段解码,脚本与样式元素内不解码 |
典型使用场景
- 把一串字符转成实体,确保任何编码环境都能原样显示
- 从网页源码里还原被实体化的中文与符号
- 生成测试样例,验证解码实现是否覆盖十六进制与十进制两种形式
- 处理只允许纯 ASCII 的通道,把中文临时实体化
- 排查显示成 &#x 开头的字面量:说明解码少走了一次
转换脚本示例
转换脚本示例
import html
# 解码:命名、十进制、十六进制三种形式一次处理
html.unescape('中中&') # '中中&'
# 反查:单个字符的码点
hex(ord('中')) # '0x4e2d'
# 编码:补足 4 位,高级字符补到 5 到 6 位
def to_hex_entity(ch):
return '&#x%04X;' % ord(ch)
to_hex_entity('中') # '中'
# 整串转换:先按码点遍历,避免代理对问题
''.join(to_hex_entity(c) for c in '中文😀')实体形式对照
| 字符 | 命名实体 | 十进制 | 十六进制 |
|---|---|---|---|
| & | & | & | & |
| < | < | < | < |
| 中 | 无命名形式 | 中 | 中 |
| 不换行空格 | |   |   |
| 笑脸 | 无命名形式 | 😀 | 😀 |
实体只在常规文本、属性值与部分上下文里被解码;script 与 style 属于原始文本元素,写在那里的 < 不会被还原成尖括号,这也是脚本内注入写法与标签内不同的原因。
用「过滤实体」当安全手段并不可靠:攻击者可以直接写真实字符,也可以用十进制、十六进制、缺分号等写法绕过匹配。正确做法是按上下文编码输出,而不是靠黑名单删实体。