如果 unicode 代码点使用 17 位或更多,则代理对如何计算?
Unicode 代码点是范围从 0x000000 到 0x10FFFF 的标量值。因此它们是 21 位整数,而不是 17 位。
代理对是UTF-16形式的一种机制。这将 21 位标量值表示为一个或两个 16 位代码单元。
Unicode 联盟的常见问题解答UTF-8、UTF-16、UTF-32 和 BOM 中通过示例代码详细解释了这一点。该常见问题解答引用了 Unicode 标准的部分,其中包含更多详细信息。
如果您需要的是代码,以下是单个代码点分别以 UTF-16 和 UTF-8 进行编码的方式。
UTF-16 代码单元的单个代码点:
if (cp < 0x10000u)
{
*out++ = static_cast<uint16_t>(cp);
}
else
{
*out++ = static_cast<uint16_t>(0xd800u + (((cp - 0x10000u) >> 10) & 0x3ffu));
*out++ = static_cast<uint16_t>(0xdc00u + ((cp - 0x10000u) & 0x3ffu));
}
UTF-8 代码单元的单个代码点:
if (cp < 0x80u)
{
*out++ = static_cast<uint8_t>(cp);
}
else if (cp < 0x800u)
{
*out++ = static_cast<uint8_t>((cp >> 6) & 0x1fu | 0xc0u);
*out++ = static_cast<uint8_t>((cp & 0x3fu) | 0x80u);
}
else if (cp < 0x10000u)
{
*out++ = static_cast<uint8_t>((cp >> 12) & 0x0fu | 0xe0u);
*out++ = static_cast<uint8_t>(((cp >> 6) & 0x3fu) | 0x80u);
*out++ = static_cast<uint8_t>((cp & 0x3fu) | 0x80u);
}
else
{
*out++ = static_cast<uint8_t>((cp >> 18) & 0x07u | 0xf0u);
*out++ = static_cast<uint8_t>(((cp >> 12) & 0x3fu) | 0x80u);
*out++ = static_cast<uint8_t>(((cp >> 6) & 0x3fu) | 0x80u);
*out++ = static_cast<uint8_t>((cp & 0x3fu) | 0x80u);
}
这是一个希望对初学者更友好的阐述。
代理代码点的范围为 0xD800-0xDF00。该空间的前半部分用于代理的上半部分,后半部分用于下半部分。
因此,要对 U+10000 进行编码,请将其分成两半,并将它们塞入可用的插槽中。
D8 00 DC 00
同样,对 U+10FFFF 进行编码,可以得到
DB FF DF FF
换句话说,从 D800 到 DBFF 的值的 D800 部分被屏蔽,余数用于代码点完整值的前半部分。类似地,从 DC00 到 DFFF 的值已屏蔽掉 DC00,余数用于编码码点的低部分。
U+0001 0000 = base 0x00010000 + 0x0000
= 00 0000 0000 00 0000 0000
mm nnnn nnnn pp qqqq qqqq
U+0010 FFFF = base 0x00010000 + 0xFFFF
= 11 1111 1111 11 1111 1111
mm nnnn nnnn pp qqqq qqqq
1101 10mm nnnn nnnn D8+xx 1110 11pp qqqq qqqq DC+yy
---------------------------- ----------------------------
1101 1000 0000 0000 D800 1110 1100 0000 0000 DC00
1101 1011 1111 1111 DBFF 1110 1111 1111 1111 DFFF