当前位置: 首页 > news >正文

对于 UTF-16 的高低代理项码点的解析

起因:

​ 首先是我写的这一段代码, 这是一个手搓的 Json 解析器内部的一个, 把转义字符还原成 UTF-16 的一个逻辑代码:

case 'u': //Unicode 字符if (i + 4 < text.Length){byte[] bytes;string u = text.Substring(i + 1, 4);if (Command.string2bytes(u, out bytes) == true){i = i + 4; //这里不因该是 i + 5, 因为下一次循环会 i++tmp_str = tmp_str + (string)char.ConvertFromUtf32((int)Command.sum4bytes(bytes));}else{tmp_str = tmp_str + "\\u";}}else{tmp_str = tmp_str + "\\u";}
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.

怎么解析:

补充代码点 => 代理项代码点
TMP = INPUT - 0x10000
HSCP = (TMP / 0x400) + 0xD800
LSCP = TMP % 0x400 + 0xDC00
代理项代码点 => 补充代码点
OUTPUT = ((HSCP - 0xD800) * 0x0400) + (LSCP - 0xDC00) + 0x010000

​ 按照以上工具, 用 C# 写的话, 如下所示

case 'u': //Unicode 字符if (i + 4 < text.Length){byte[] bytes;string u = text.Substring(i + 1, 4);if (Command.TryString2Bytes(u, out bytes) == true){int hex = (int)Command.Sum4Bytes(bytes);//对于代理位解析, 参看一下连接, 感谢!//https://zhuanlan.zhihu.com/p/147339588//https://www.cnblogs.com/sailJs/p/18397567if (hex > 55295 && hex < 56320) // D800 到 DBFF //高代理位 //emoji 那些{if(i + 10 < text.Length && text[i + 5] == '\\' && text[i + 6] == 'u'){u = text.Substring(i + 7, 4);if (Command.TryString2Bytes(u, out bytes) == true){int HSCP = hex;int LSCP = (int)Command.Sum4Bytes(bytes);if (LSCP < 56320 || LSCP > 57343) //低代理位, 在 DC00 到 DFFF {tmp_str = tmp_str + "\\u";}else{//HSCP = HSCP - (int)Command.Sum4Bytes(Command.String2Bytes("D800"));HSCP = HSCP - 55296;HSCP = HSCP * 1024;//LSCP = LSCP - (int)Command.Sum4Bytes(Command.String2Bytes("DC00"));LSCP = LSCP - 56320;//hex = HSCP + LSCP + (int)Command.Sum4Bytes(Command.String2Bytes("010000"));hex = HSCP + LSCP + 65536;i = i + 10;tmp_str = tmp_str + (string)char.ConvertFromUtf32(hex);}}else{tmp_str = tmp_str + "\\u";}}else{tmp_str = tmp_str + "\\u";}}else {i = i + 4; //这里不因该是 i + 5, 因为下一次循环会 i++tmp_str = tmp_str + (string)char.ConvertFromUtf32(hex);}}else{tmp_str = tmp_str + "\\u";}}else{tmp_str = tmp_str + "\\u";}break;

​ 总之就是, 在俺不了解代理项之前, 俺一直以为一个 \uXXXX 代表一个 char, 了解代理项之后就明白, 只要遇到一个处于高代理项的字符时, 它的后面必定会有一个低代理项 (即两个 char).

参考连接:

  • Unicode | 代理项(Surrogate)

  • Unicode编码 - 代理区和4字节codePoint

http://www.jsqmd.com/news/425044/

相关文章:

  • 具身智能2026年产业图谱:智平方引领下的技术分化与价值落地
  • 春晚带火具身智能!2026 核心技术推荐 五强企业解锁智能新未来
  • PMMA塑料光纤怎么提高耐热性
  • 【系统分析师】10.4 问题分析
  • DSA期末考情分析
  • 实战笔记】手把手拆解S7-1200四轴伺服控制系统
  • 2026线上英语启蒙课实测对比:这几家最靠谱,家长闭眼选不踩坑 - 品牌测评鉴赏家
  • 线上英语机构剑桥授权教材真假难辨?避坑指南,家长直接抄作业 - 品牌测评鉴赏家
  • 小学生剑桥原版教材线上课推荐|家长实测不踩坑,选课直接抄作业 - 品牌测评鉴赏家
  • 基于 Lexical 实现变量输入编辑器
  • 5个优质线上少儿英语平台推荐!家长闭眼抄作业,选课不踩坑 - 品牌测评鉴赏家
  • 实测精选!2026少儿剑桥英语线上机构推荐,避坑不花冤枉钱 - 品牌测评鉴赏家
  • 中考英语提分慌?4家高口碑辅导机构实测推荐!家长闭眼冲不踩坑 - 品牌测评鉴赏家
  • LangGraph4j 学习系列(6)-并行工作流
  • 不踩坑!2026初中英语辅导机构实测推荐,家长闭眼抄作业 - 品牌测评鉴赏家
  • 2026小学英语辅导机构推荐|家长必看!避开坑,选对机构少走弯路 - 品牌测评鉴赏家
  • 盘点|2026十大高中语文教育机构!家长选课不踩坑,提分有方向 - 品牌测评鉴赏家
  • 高考阅读平均分58%?实测4家顶尖线上机构,避坑不踩雷 - 品牌测评鉴赏家
  • 高考文言文翻译难提分?这几家线上机构帮你逆袭 - 品牌测评鉴赏家
  • 高中文言文线上辅导哪家强?实测4家靠谱机构,避坑不花冤枉钱 - 品牌测评鉴赏家
  • 3 月做题记录
  • 新高考语文写作提分攻略:6家宝藏线上机构实测,精准击破4大写作痛点! - 品牌测评鉴赏家
  • 高中语文阅读提分难?实测五家热门线上机构,避坑指南+精准推荐 - 品牌测评鉴赏家
  • 高中语文阅读提分秘籍:线上辅导机构大揭秘 - 品牌测评鉴赏家
  • 救命!高中语文不用瞎刷题这5个神仙平台,从基础到拔高全搞定 - 品牌测评鉴赏家
  • 齿轮齿条转向器装配图(CAD)
  • 豆浆机Creo【结构详细,但装配图不可以编辑】
  • oeasy blender 016 晴天娃娃
  • LangGraph4j 学习系列(5)-Hook勾子
  • 三傻排序