对于 UTF-16 的高低署理项码点的剖析

[复制链接]
发表于 2026-3-2 06:24:15 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
因由:

​        起首是我写的这一段代码, 这是一个手搓的 Json 剖析器内部的一个, 把转义字符还原成 UTF-16 的一个逻辑代码:
  1. case 'u': //Unicode 字符
  2.     if (i + 4 < text.Length)
  3.     {
  4.         byte[] bytes;
  5.         string u = text.Substring(i + 1, 4);
  6.         if (Command.string2bytes(u, out bytes) == true)
  7.         {
  8.             i = i + 4; //这里不因该是 i + 5, 因为下一次循环会 i++
  9.             tmp_str = tmp_str + (string)char.ConvertFromUtf32((int)Command.sum4bytes(bytes));
  10.         }
  11.         else
  12.         {
  13.             tmp_str = tmp_str + "\\u";
  14.         }
  15.     }
  16.     else
  17.     {
  18.         tmp_str = tmp_str + "\\u";
  19.     }
  20. break;
复制代码
​        正常来说它可以把一些雷同于 \u5bd2\u5bd2\u5929\u4e0b\u7b2c\u4e00\u53ef\u7231\uff01 如许的 Unicode 给转换成 寒寒天下第一可爱!, 但是碰到 emoji 的文本时, 就抛出非常了:
System.ArgumentOutOfRangeException:“有用的 UTF32 值介于 0x000000 和 0x10ffff 之间(包罗这两者),而且不能包罗署理项码位值(0x00d800 ~ 0x00dfff)。”
​        嗯, 没见过的非常, 头一次见到署理项码位值, 然后在网路上查了一下这个玩意
什么是署理项:

​        在通例 UTF-16 编码中, 文本是以 2 字节单位储存的, 满意一些常用字符的体现. 但是对于 emoji 和其他生僻字符却不敷用, 以是, 在原有的 0x0000 至 0xFFFF 区间, 分别了一些地区给署理项使用, 然后通过署理项转换末了映射为增补码点.

  • 起首是高署理项码点, 它是辨认拓展字符的关键, 区间在 0xD800 至 0xDBFF.
  • 尾随高署理项的是低署理项码点, 区间在 0xDC00 至 0xDFFF.
  • 增补码点, 用于拓展一些不常见的生僻的字符, 大概 emoji 及其他符号, 署理项终极要映射在此区间, 区间在 0x010000 至 0x10FFFF.
怎么剖析:

增补代码点 => 署理项代码点
  1. TMP = INPUT - 0x10000
  2. HSCP = (TMP / 0x400) + 0xD800
  3. LSCP = TMP % 0x400 + 0xDC00
复制代码
署理项代码点 => 增补代码点
  1. OUTPUT = ((HSCP - 0xD800) * 0x0400) + (LSCP - 0xDC00) + 0x010000
复制代码
​        按照以上流程, 用 C# 写的话, 如下所示
  1. case 'u': //Unicode 字符
  2.     if (i + 4 < text.Length)
  3.     {
  4.         byte[] bytes;
  5.         string u = text.Substring(i + 1, 4);
  6.         if (Command.TryString2Bytes(u, out bytes) == true)
  7.         {
  8.             int hex = (int)Command.Sum4Bytes(bytes);
  9.             //对于代理位解析, 参看一下连接, 感谢!
  10.             //https://zhuanlan.zhihu.com/p/147339588
  11.             //https://www.cnblogs.com/sailJs/p/18397567
  12.             if (hex > 55295 && hex < 56320) // D800 到 DBFF //高代理位 //emoji 那些
  13.             {
  14.                 if(i + 10 < text.Length && text[i + 5] == '\\' && text[i + 6] == 'u')
  15.                 {
  16.                     u = text.Substring(i + 7, 4);
  17.                     if (Command.TryString2Bytes(u, out bytes) == true)
  18.                     {
  19.                         int HSCP = hex;
  20.                         int LSCP = (int)Command.Sum4Bytes(bytes);
  21.                         if (LSCP < 56320 || LSCP > 57343) //低代理位, 在 DC00 到 DFFF
  22.                         {
  23.                             tmp_str = tmp_str + "\\u";
  24.                         }
  25.                         else
  26.                         {
  27.                             //HSCP = HSCP - (int)Command.Sum4Bytes(Command.String2Bytes("D800"));
  28.                             HSCP = HSCP - 55296;
  29.                             HSCP = HSCP * 1024;
  30.                             //LSCP = LSCP - (int)Command.Sum4Bytes(Command.String2Bytes("DC00"));
  31.                             LSCP = LSCP - 56320;
  32.                             //hex = HSCP + LSCP + (int)Command.Sum4Bytes(Command.String2Bytes("010000"));
  33.                             hex = HSCP + LSCP + 65536;
  34.                             i = i + 10;
  35.                             tmp_str = tmp_str + (string)char.ConvertFromUtf32(hex);
  36.                         }
  37.                     }
  38.                     else
  39.                     {
  40.                         tmp_str = tmp_str + "\\u";
  41.                     }
  42.                 }
  43.                 else
  44.                 {
  45.                     tmp_str = tmp_str + "\\u";
  46.                 }
  47.             }
  48.             else
  49.             {
  50.                 i = i + 4; //这里不因该是 i + 5, 因为下一次循环会 i++
  51.                 tmp_str = tmp_str + (string)char.ConvertFromUtf32(hex);
  52.             }
  53.         }
  54.         else
  55.         {
  56.             tmp_str = tmp_str + "\\u";
  57.         }
  58.     }
  59.     else
  60.     {
  61.         tmp_str = tmp_str + "\\u";
  62.     }
  63.     break;
复制代码
​        总之就是, 在俺不相识署理项之前, 俺不绝以为一个 \uXXXX 代表一个 char, 相识署理项之后就明确, 只要碰到一个处于高署理项的字符时, 它的背面肯定会有一个低署理项 (即两个 char).
参考毗连:


免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金.
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表