Unicode 字符串转 UTF-8 编码算法剖析

📊 Unicode 字符串转 UTF-8 编码算法剖析

——从 C# char 到 C++ wchar_t 的编码转换原理

引用:UTF-8 编解码可视化分析


🔍 1. 算法功能概述

该函数将 Unicode 字符串(C# string)转换为 UTF-8 编码的字节数组,同时输出有效字节数 count
核心流程

  1. 计算 UTF-8 编码所需字节数
  2. 分配缓冲区(额外预留 3 字节)
  3. 逐字符编码为 UTF-8 字节序列
开始
计算字节数
分配缓冲区
编码转换
返回结果

🧠 2. 算法原理与 UTF-8 编码规则

UTF-8 是变长编码,规则如下:

在这里插入图片描述

Unicode 码点范围UTF-8 字节序列结构字节数位模式图示
U+0000 ~ U+007F0xxxxxxx1[0xxx xxxx]
U+0080 ~ U+07FF110xxxxx 10xxxxxx2[110x xxxx] [10xx xxxx]
U+0800 ~ U+FFFF1110xxxx 10xxxxxx 10xxxxxx3[1110 xxxx] [10xx xxxx] [10xx xxxx]

💡 :C# char 为 16 位 Unicode,C++ wchar_t 在 Windows 中与之等价(Linux 为 32 位)。


🔄 3. 算法流程详解
步骤 1:计算 UTF-8 字节数

在这里插入图片描述

int k = 0;
while (i < s.Length) {char ch = s[i++];if (ch < 0x80) k++;        // ASCII 字符(1 字节)else if (ch < 0x800) k += 2; // 2 字节字符else if (ch < 0x10000) k += 3; // 3 字节字符(含 CJK 字符)
}
count = k; // 输出有效字节数
  • 时间复杂度:O(n),需遍历整个字符串。
步骤 2:分配缓冲区
buf = new byte[count + 3]; // 额外分配 3 字节(安全填充)
  • 为什么 +3?
    预留空间防止溢出(UTF-8 单字符最多占 3 字节),确保写入安全。
步骤 3:编码转换(核心)

在这里插入图片描述

fixed (byte* p = buf) { // 固定内存地址(避免 GC 移动)while (i < s.Length) {char ch = s[i++];// 1 字节编码(ASCII)if (ch < 0x80) p[k++] = (byte)(ch & 0xFF); // 2 字节编码(拉丁字母扩展)else if (ch < 0x800) {p[k++] = (byte)(((ch >> 6) & 0x1F) | 0xC0); // 110xxxxxp[k++] = (byte)((ch & 0x3F) | 0x80);        // 10xxxxxx}// 3 字节编码(中/日/韩文字符)else if (ch < 0x10000) {p[k++] = (byte)(((ch >> 12) & 0x0F) | 0xE0); // 1110xxxxp[k++] = (byte)(((ch >> 6) & 0x3F) | 0x80); // 10xxxxxxp[k++] = (byte)((ch & 0x3F) | 0x80);         // 10xxxxxx}}
}
  • 位操作解析
    原始字符
    位分解
    添加前缀
    组合字节

⚠️ 4. 算法局限性
算法局限
不支持代理对
缓冲区浪费
缺少4字节编码
无法处理表情符号
多分配3字节
不支持补充平面
  • 不支持代理对(Surrogate Pairs)
    无法处理 U+10000 以上的字符(如 emoji 𠮷),需补充 4 字节编码逻辑。
  • 缓冲区浪费:多分配 3 字节(可通过精确计算优化)。

📝 5. 完整代码(含逐行注释)
public static byte[] GetUTF8StringBuffer(string s, out int count)
{count = 0; // 初始化输出参数if (s == null){throw new ArgumentNullException("s"); // 空字符串检查}byte[] buf = null; // 存储结果的字节数组int k = 0;         // 临时计数器(字节数/写入位置)int i = 0;         // 字符串遍历索引// === 步骤 1:计算 UTF-8 编码所需字节数 ===while (i < s.Length){char ch = s[i++]; // 逐个读取字符if (ch < 0x80)    // ASCII 字符(0x00 ~ 0x7F){k++;          // 占 1 字节}else if (ch < 0x800) // 0x80 ~ 0x7FF(拉丁字母扩展){k += 2;       // 占 2 字节}else if (ch < 0x10000) // 0x800 ~ 0xFFFF(CJK 字符等){k += 3;       // 占 3 字节}}// === 步骤 2:分配缓冲区(额外 +3 字节) ===buf = new byte[(count = k) + 3]; // count 赋值为 k,总长度 = k+3// === 步骤 3:编码转换(不安全代码块) ===unsafe // 启用不安全代码{fixed (byte* p = buf) // 固定缓冲区内存地址{i = 0; // 重置字符串索引k = 0; // 重置字节数组索引while (i < s.Length){char ch = s[i++]; // 读取下一个字符// --- 1 字节编码 ---if (ch < 0x80){p[k++] = (byte)(ch & 0xFF); // 直接存储低 8 位}// --- 2 字节编码 ---else if (ch < 0x800){// 构造首字节:110xxxxx (0xC0 | 高 5 位)p[k++] = (byte)(((ch >> 6) & 0x1F) | 0xC0);// 构造次字节:10xxxxxx (0x80 | 低 6 位)p[k++] = (byte)((ch & 0x3F) | 0x80);}// --- 3 字节编码 ---else if (ch < 0x10000){// 构造首字节:1110xxxx (0xE0 | 高 4 位)p[k++] = (byte)(((ch >> 12) & 0x0F) | 0xE0);// 构造次字节:10xxxxxx (0x80 | 中 6 位)p[k++] = (byte)(((ch >> 6) & 0x3F) | 0x80);// 构造尾字节:10xxxxxx (0x80 | 低 6 位)p[k++] = (byte)((ch & 0x3F) | 0x80);}}}}return buf; // 返回 UTF-8 字节数组
}

🎯 6. 总结
算法价值
高效编码转换
跨语言兼容
内存安全
优化方向
支持4字节编码
精确缓冲区分配
使用Span提升性能
  • 核心价值:高效实现 Unicode 到 UTF-8 的转换,适用于网络传输或跨语言交互。
  • 优化方向
    1. 支持 4 字节编码(代理对)
    2. 移除多余缓冲区分配
    3. 使用 Span<byte> 提升性能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/93939.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/93939.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php的安全性到底怎么样

PHP作为一种流行的服务器端脚本语言&#xff0c;被广泛应用于Web开发。然而&#xff0c;由于PHP是一种较为灵活的语言&#xff0c;其安全性议题一直备受争议。在这篇文章中&#xff0c;我将从多个方面来讨论PHP的安全性&#xff0c;包括常见的安全漏洞、防范措施以及最佳实践。…

mapbox高阶,结合threejs(threebox)添加建筑glb模型,添加阴影效果,设置阴影颜色和透明度

👨‍⚕️ 主页: gis分享者 👨‍⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨‍⚕️ 收录于专栏:mapbox 从入门到精通 文章目录 一、🍀前言 1.1 ☘️mapboxgl.Map 地图对象 1.2 ☘️mapboxgl.Map style属性 1.3 ☘️threebox loadObj加载模型 二、🍀…

SSM从入门到实战:1.6 Spring数据访问与JDBC模板

&#x1f44b; 大家好&#xff0c;我是 阿问学长&#xff01;专注于分享优质开源项目解析、毕业设计项目指导支持、幼小初高的教辅资料推荐等&#xff0c;欢迎关注交流&#xff01;&#x1f680; 06-Spring数据访问与JDBC模板 &#x1f4d6; 本文概述 本文是SSM框架系列Spri…

下一代IT服务管理:ITIL5会是什么样?

ITIL4发布到现在也就5年多时间&#xff0c;按照以往的更新节奏&#xff0c;ITIL5最早也得2027年之后。但现在IT发展的速度&#xff0c;跟以前完全不是一个量级。AI都快把我们的饭碗抢了&#xff08;开个玩笑&#xff09;&#xff0c;ITIL要是还按部就班&#xff0c;估计真要被时…

最新研究进展:2023-2025年神经机器翻译突破性成果

文章目录 一、模型架构创新 1.1 混合架构的崛起 1.2 多模态翻译的突破 1.3 大语言模型与NMT的深度融合(2023-2024) 1.4 非自回归翻译(NAT)的效率革命(2024) 二、数据与训练策略优化 2.1 低资源语言翻译的飞跃 2.2 动态数据增强技术 三、效率与部署 3.1 模型压缩与加速 3.…

OpenTelemetry WebSocket 监控终极方案:打通最后一公里

概述 OpenTelemetry&#xff0c;以下简称 OTEL&#xff0c;是由 CNCF 托管的“一站式可观测性标准”&#xff0c;把指标、链路、日志三大信号统一为单一 SDK/API&#xff0c;零侵入地采集从浏览器、移动端到后端、容器、云服务的全栈遥测数据&#xff0c;并支持 40 后端一键导…

VS Code 出现的 Web 视图加载错误和服务工作者注册失败问题解决方案

针对 VS Code 或 Cursor &#xff08;vscode系&#xff09;中出现的 Web 视图加载错误和服务工作者注册失败问题&#xff0c;以下是永久性解决方案的完整操作指南&#xff1a;解决方案步骤打开命令面板 使用快捷键 CtrlShiftP&#xff08;Windows/Linux&#xff09;或 CmdShift…

【qml-4】qml与c++交互(类型多例)

背景&#xff1a; 【qml-1】qml与c交互第一次尝试&#xff08;实例注入&#xff09; 【qml-2】尝试一个有模式的qml弹窗 【qml-3】qml与c交互第二次尝试&#xff08;类型注册&#xff09; 【qml-4】qml与c交互&#xff08;类型多例&#xff09; 【qml-5】qml与c交互&#…

图数据库如何构筑 Web3 风控防线 聚焦批量注册与链上盗转 悦数图数据库

随着 Web3 生态的不断演进&#xff0c;链上风险呈现出团伙化、隐蔽化和动态化的趋势&#xff0c;传统的单点风控手段已难以应对复杂多变的攻击模式。尤其在批量注册薅羊毛与链上交易盗转洗钱等高频风险场景中&#xff0c;攻击者往往通过伪造身份、跨链操作、多层嵌套转账等方式…

恒流源电路学习

恒流源的设计原理&#xff1a; 如图所示你可以看到右边的的推到公式得到红点处的电压是一个和左边相关的定值&#xff0c;所以呢右边的电流就是电压除以那个4Ω&#xff0c;所以得到右边的电路的电流大体是一个定值&#xff0c;不管你再加什么东西都可以保持这个电流&#xff…

基于生成对抗网络的模糊图像恢复原理与技术实现

1. 引言图像模糊是数字图像处理中的常见问题&#xff0c;其成因包括相机抖动、物体运动、聚焦不良等。传统方法如维纳滤波、Lucy-Richardson 算法等依赖于模糊核估计和逆滤波&#xff0c;在复杂场景下性能有限。生成对抗网络&#xff08;Generative Adversarial Networks, GAN&…

【Doris 系列】Doris IP 变更修复

FE 恢复 异常日志 查看 fe.out 会有以下报错&#xff0c;此时 fe 进程是无法启动的&#xff0c;操作前注意备份所有 fe 的元数据并停止上游读写动作&#xff01; java.io.IOException: the self host 192.168.31.78 does not equal to the host in ROLE file 192.168.31.81. Yo…

安卓14系统应用收不到开机广播

安卓14系统应用收不到开机广播 - Wesley’s Blog 前段时间有测试反馈在安卓14 上面某系统应用恢复出厂设置后没有自启动&#xff0c;究竟是什么原因呢&#xff1f; 回顾 Android 从3.1开始&#xff0c;会将新安装并且从未被启动的应用置为“STOPPED”状态&#xff0c;或者被…

C# Attribute 方法扩展

场景 刚写完一个干净利落的方法&#xff0c;比如保存数据到数据库&#xff0c;逻辑清晰、结构优雅&#xff0c; 第二天&#xff0c;“嘿&#xff0c;保存完数据&#xff0c;记得给客户发个邮件哦~” 第三天&#xff0c;“能不能再发个消息通知其他系统&#xff1f;” 第四天&am…

【URP】[法线贴图]为什么主要是蓝色的?

【从UnityURP开始探索游戏渲染】专栏-直达 法线贴图呈现蓝紫色调&#xff08;尤其以蓝色为主&#xff09;是由其‌存储原理、切线空间坐标系设计及颜色编码规则共同决定的‌。 核心原因&#xff1a;法线向量的存储规则‌ ‌法线向量的物理范围‌ 法线是单位向量&#xff0c;…

驱动开发系列63 - NVIDIA 开源GPU驱动open-gpu-kernel-modules编译调试

目录 一:通过apt方式安装nvidia 驱动 二:通过 .run 方式安装nvidia驱动 三:编译安装nvidia开源内核驱动 四:验证和调试 五:卸载驱动 1. 以apt方式安装nvidia 驱动的卸载方法 2. 以.run方式安装nvidia驱动的卸载方法 六:安装CUDA环境 一:通过apt方式安装nvidia 驱动…

对KingbaseES架构的解析:从读写分离到异地灾备的技术实现与保障机制

声明&#xff1a;文章为本人真实测评博客&#xff0c;非广告&#xff0c;并没有推广该平台 &#xff0c;为用户体验文章 本人旨在分享最真实的用户体验&#xff0c;为关注此类产品的朋友们提供一个客观的参考。 文章目录一、架构全景&#xff1a;四级高可用构建数字基础1.1 物…

Visual Studio中的常用调试功能(上)

1、利用断点进行调试添加断点的方式有以下几种1.键盘快捷键F92.通过菜单【Debug&#xff08;调试&#xff09;】-》【Toggle BreakPoint&#xff08;切换断点&#xff09;】3.点击代码行左边的空白处&#xff08;推荐&#xff09;设置断点后&#xff0c;按F5运行程序&#xff0…

Linux -- 线程同步

1.1条件变量 (1)当⼀个线程互斥地访问某个变量时&#xff0c;它可能发现在其它线程改变状态之前&#xff0c;它什么也做不了。 (2)例如⼀个线程访问队列时&#xff0c;发现队列为空&#xff0c;它只能等待&#xff0c;只到其它线程将⼀个节点添加到队列 中。这种情况就需要⽤到…

前端进阶指南:JavaScript性能优化实战全解析

深入剖析 JavaScript 性能瓶颈&#xff0c;分享优化技巧与最佳实践&#xff0c;让你的前端应用更快、更稳、更流畅。 &#x1f4d1; 目录 一、前言 二、性能瓶颈的常见来源 三、JavaScript代码优化技巧 1. 避免重复计算 2. 合理使用防抖与节流 3. 使用事件委托 四、渲染…