16.6.3 解码数字字符引用
数字字符引用的格式是「前缀 + Unicode 码点」;解码的关键是提取出码点,再做一番合法性检查,最后用
String.fromCodePoint还原为字符。
一、提取码点
- 前缀有两种:
&#(十进制)与&#x(十六进制); - 判断进制后选用不同正则,
body[1]就是码点字符串:
const hex = head[0] === '&#x'
const pattern = hex ? /^&#x([0-9a-f]+);?/i : /^&#([0-9]+);?/
const body = pattern.exec(rawText)
if (body) {
// 按对应进制把码点字符串转成数字
let cp = Number.parseInt(body[1], hex ? 16 : 10)
// ...码点合法性检查
decodedText += String.fromCodePoint(cp)
// 消费整个数字字符引用
advance(body[0].length)
} else {
// 没有匹配:不解码,把 head[0] 原样拼上并消费
decodedText += head[0]
advance(head[0].length)
}
二、码点的合法性检查
WHATWG 规范对哪些码点是解析错误有明确定义,命中后按下表处理:
| 码点范围 | 含义 | 处理方式 |
|---|---|---|
0x00 | 空字符 | 替换为 0xfffd |
> 0x10ffff | 超过 Unicode 最大值 | 替换为 0xfffd |
0xd800 ~ 0xdfff | surrogate pair 范围 | 替换为 0xfffd |
0xfdd0 ~ 0xfdef、(cp & 0xfffe) === 0xfffe | noncharacter | 什么都不做,交给平台处理 |
| 控制字符集 | [0x01, 0x1f] 与 [0x7f, 0x9f] | 查 CCR_REPLACEMENTS 表替换,查不到则用原码点 |
控制字符集的判定细节:
- 范围是
[0x01, 0x1f]加上[0x7f, 0x9f]; - 需要去掉 ASCII 空白符
0x09(TAB)、0x0A(LF)、0x0C(FF); - 但 WHATWG 规范要求保留
0x0D(CR),即0x0D也要走替换表。
if (cp === 0) {
cp = 0xfffd
} else if (cp > 0x10ffff) {
cp = 0xfffd
} else if (cp >= 0xd800 && cp <= 0xdfff) {
cp = 0xfffd
} else if ((cp >= 0xfdd0 && cp <= 0xfdef) || (cp & 0xfffe) === 0xfffe) {
// noncharacter:noop
} else if (
(cp >= 0x01 && cp <= 0x08) ||
cp === 0x0b ||
(cp >= 0x0d && cp <= 0x1f) ||
(cp >= 0x7f && cp <= 0x9f)
) {
cp = CCR_REPLACEMENTS[cp] || cp
}
0xfffd对应的符号是�,它是 Unicode 的替换字符,表示解码过程出错(出现「乱码」时常见的就是它)。
三、CCR_REPLACEMENTS 替换表(节选)
const CCR_REPLACEMENTS = {
0x80: 0x20ac, // €
0x82: 0x201a,
0x83: 0x0192,
0x84: 0x201e,
0x85: 0x2026,
0x86: 0x2020,
0x87: 0x2021,
0x88: 0x02c6,
0x89: 0x2030,
0x8a: 0x0160,
0x8b: 0x2039,
0x8c: 0x0152,
0x8e: 0x017d,
0x91: 0x2018,
0x92: 0x2019,
0x93: 0x201c,
0x94: 0x201d,
0x95: 0x2022,
0x96: 0x2013,
0x97: 0x2014,
0x98: 0x02dc,
0x99: 0x2122,
0x9a: 0x0161,
0x9b: 0x203a,
0x9c: 0x0153,
0x9e: 0x017e,
0x9f: 0x0178
}
易错点:
0x09、0x0A、0x0C是 ASCII 空白符、不替换,而0x0D(CR) 虽然也是 ASCII 空白符却必须替换,别把它一起排除了。
相关
- 16.6.2 解码命名字符引用:
decodeHtml的主循环与命名分支,本节的数字分支接在它的else中 - 16.5 解析属性:属性值同样需要解码字符引用