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

UTF-8 编解码可视化分析

🌟 UTF-8 编解码可视化分析

引用

  1. openppp2/ppp/text/Encoding.cpp
  2. C# UTF-8字符集长度获取及判断二进制块是否UTF-8编码字符集算法的实现
  3. 严格的C风格字符串 Unicode To UTF-8 的实现(C#、JavaScript)
  4. nsjsdotnet/NSJSString.cs
📐 1. UTF-8 字节对齐原理 (GetUtf8Alignment)

在这里插入图片描述

📊 UTF-8 字符编码结构
字节数首字节范围编码模板
10x00-0x7F0xxxxxxx
20xC0-0xDF110xxxxx 10xxxxxx
30xE0-0xEF1110xxxx 10xxxxxx 10xxxxxx
40xF0-0xF711110xxx 10xxxxxx 10xxxxxx 10xxxxxx

🔢 2. 字符计数流程 (GetUtf8BufferCount)

1
2
3
4
起始指针
是否可读内存?
返回 0
获取首字节
计算对齐字节数
对齐数
读取单字节
读取双字节
读取三字节
读取四字节
字符=0?
计算长度
移动指针

✅ 3. UTF-8 验证流程 (IsUTF8Buffer)

结束
初始化计数器=1
遍历字节
计数器=1?
字节>=0x80?
计算连续1个数
个数=1 OR >4?
返回 false
设置计数器
后两位=10?
计数器-1
计数器>0?
计数器重置为1
计数器=1?
返回 true

⚙️ C++ 实现代码 (带详细注释)

#pragma execution_character_set("utf-8")  // 指示编译器使用UTF-8编码执行字符集#include <Windows.h>  // 包含Windows API头文件#include <string>     // 包含C++字符串库
#include <cstring>    // 包含C风格字符串操作函数
#include <iostream>   // 包含输入输出流对象
#include <codecvt>    // 包含字符编码转换工具// 定义位掩码常量用于UTF-8编码检测
constexpr BYTE kFirstBitMask = 0x80;  // 10000000b - 检测首字节最高位
constexpr BYTE kSecondBitMask = 0x40; // 01000000b - 检测首字节次高位
constexpr BYTE kThirdBitMask = 0x20;  // 00100000b - 检测首字节第三位
constexpr BYTE kFourthBitMask = 0x10; // 00010000b - 检测首字节第四位// 计算UTF-8字符的字节长度
int GetUtf8Alignment(BYTE character) {int alignment = 1;  // 默认ASCII字符占1字节// 检查首字节最高位是否为1(表示多字节字符)if ((character & kFirstBitMask) > 0) {// 检查首字节第三位是否为1(可能为3字节字符)if ((character & kThirdBitMask) > 0) {// 检查首字节第四位是否为1(可能为4字节字符)if ((character & kFourthBitMask) > 0) {alignment = 4;  // 确定为4字节字符}else {alignment = 3;  // 确定为3字节字符}}else {alignment = 2;  // 确定为2字节字符}}return alignment;  // 返回字符占用的字节数
}// 计算UTF-8字符串的字节长度(包含空终止符)
int GetUtf8BufferCount(BYTE* s) {if (NULL == s) {  // 空指针检查return 0;}BYTE* i = s;  // 创建指针用于遍历字符串// 遍历直到遇到空终止符while (*i != '\x0') {int alignment = GetUtf8Alignment(*i);  // 获取当前字符的字节长度int character = 0;  // 存储字符值(实际未使用)// 根据字符长度处理不同情况if (alignment == 1) {character = *i++;  // 单字节字符直接读取}else if (alignment == 2) {character = *(short*)i;  // 双字节字符(存在字节序问题)i += 2;  // 指针前进2字节}else if (alignment == 3) {// 拼接三字节字符(小端序处理)character = (*i++ | *i++ << 8 | *i++ << 16);}else if (alignment == 4) {character = *(int*)i;  // 四字节字符(存在字节序问题)i += 4;  // 指针前进4字节}else {return 0;  // 无效字符长度处理}}int len = (int)(i - s);  // 计算从开始到结束的字节数(含空终止符)return len < 0 ? 0 : len;  // 处理长度小于0的情况
}// 验证缓冲区是否为有效的UTF-8编码
bool IsUTF8Buffer(BYTE* buffer, int count) {// 空指针或无效长度检查if (NULL == buffer || count <= 0) {return false;}int counter = 1;  // 当前字符的剩余字节数(初始为1)BYTE key = 0;     // 当前处理的字节for (int i = 0; i < count; i++) {key = buffer[i];  // 获取当前字节if (counter == 1) {  // 处理新字符的首字节if (key >= 0x80) {  // 检查是否多字节字符// 计算后续字节数(通过左移检测连续高位1)while (((key <<= 1) & 0x80) != 0) {counter++;  // 每多一个高位1则增加计数}// 验证字节数在有效范围(2-4字节)if (counter == 1 || counter > 4) {return false;  // 无效的首字节格式}}}else {  // 处理连续字节// 检查是否为有效的连续字节(格式应为10xxxxxx)if ((key & 0xC0) != 0x80) {return false;  // 无效的连续字节格式}counter--;  // 减少剩余字节计数}}return !(counter > 1);  // 检查是否完整读取最后一个字符
}// 将宽字符串转换为UTF-8编码的字符串
std::string wstring_to_utf8(const std::wstring& s) noexcept {// 创建宽字符到UTF-8的转换器std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;// 执行转换并返回结果return converter.to_bytes(s);
}// 程序主入口
int main(int argc, const char** argv) noexcept {const char str[] = "中国";  // UTF-8编码的中文字符串// 验证字符串是否为有效UTF-8编码std::cout << "Is utf8 string: "<< IsUTF8Buffer((BYTE*)str, sizeof(str))<< std::endl;// 计算字符串的字节长度(包含空终止符)std::cout << "Utf8 string length: "<< GetUtf8BufferCount((BYTE*)str)<< std::endl;return 0;  // 程序正常退出
}

⚡ C# 实现说明 (优化版)

// 导入kernel32.dll中的IsBadReadPtr函数,用于检测内存指针是否可读
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
private extern static bool IsBadReadPtr(void* lp, uint ucb);// 定义UTF-8编码检测用的位掩码常量
private const byte kFirstBitMask = 0x80;  // 10000000b - 检测首字节最高位
private const byte kSecondBitMask = 0x40; // 01000000b - 检测首字节次高位
private const byte kThirdBitMask = 0x20;  // 00100000b - 检测首字节第三位
private const byte kFourthBitMask = 0x10; // 00010000b - 检测首字节第四位
private const byte kFifthBitMask = 0x08;  // 00001000b - 检测首字节第五位// 计算UTF-8字符的字节长度
public static int GetUtf8Alignment(byte character)
{int alignment = 1;  // 默认ASCII字符占1字节// 检查首字节最高位是否为1(表示多字节字符)if ((character & kFirstBitMask) > 0) {// 检查首字节第三位是否为1(可能为3字节字符)if ((character & kThirdBitMask) > 0) {// 检查首字节第四位是否为1(可能为4字节字符)if ((character & kFourthBitMask) > 0) {alignment = 4;  // 确定为4字节字符}else{alignment = 3;  // 确定为3字节字符}}else{alignment = 2;  // 确定为2字节字符}}return alignment;  // 返回字符占用的字节数
}// 计算UTF-8字符串的字节长度(不安全代码)
public static unsafe int GetUtf8BufferCount(byte* s)
{if (s == null)  // 空指针检查{return 0;}byte* i = s;  // 创建指针用于遍历字符串// 循环直到遇到无效内存或空字符while (!IsBadReadPtr(i, 1))  // 检查当前指针是否可读{int alignment = GetUtf8Alignment(*i);  // 获取当前字符的字节长度int character = 0;  // 存储字符值(实际未使用)// 根据字符长度处理不同情况if (alignment == 1){character = *i++;  // 单字节字符直接读取}if (alignment == 2)  // 注意:此处应为else if,否则逻辑错误{character = *(short*)i;  // 双字节字符(存在字节序问题)i += 2;  // 指针前进2字节}else if (alignment == 3){// 拼接三字节字符(小端序处理)character = (*i++ | *i++ << 8 | *i++ << 16);}else if (alignment == 4){character = *(int*)i;  // 四字节字符(存在字节序问题)i += 4;  // 指针前进4字节}// 检测到空终止符时结束遍历if (character == 0){// 计算从开始到结束的字节数(含空终止符)int len = unchecked((int)(i - (s + 1)));return len < 0 ? 0 : len;  // 处理长度小于0的情况}}return 0;  // 遇到无效内存时返回0
}// UTF-8验证方法(字节数组重载)
public static unsafe bool IsUTF8Buffer(byte[] buffer)
{if (buffer == null)  // 空数组检查{return false;}// 固定字节数组内存地址fixed(byte * pinned = buffer){// 调用指针版本验证方法return IsUTF8Buffer(pinned, buffer.Length);}
}// UTF-8验证方法(指针版本)
public static unsafe bool IsUTF8Buffer(byte* buffer, int count)
{// 空指针或无效长度检查if (buffer == null || count <= 0){return false;}int counter = 1;  // 当前字符的剩余字节数(初始为1)byte key = 0;     // 当前处理的字节for (int i = 0; i < count; i++){key = buffer[i];  // 获取当前字节if (counter == 1)  // 处理新字符的首字节{if (key >= 0x80)  // 检查是否多字节字符{// 计算后续字节数(通过左移检测连续高位1)while (((key <<= 1) & 0x80) != 0){counter++;  // 每多一个高位1则增加计数}// 验证字节数在有效范围(2-4字节)if (counter == 1 || counter > 4){return false;  // 无效的首字节格式}}}else  // 处理连续字节{// 检查是否为有效的连续字节(格式应为10xxxxxx)if ((key & 0xC0) != 0x80){return false;  // 无效的连续字节格式}counter--;  // 减少剩余字节计数}}return !(counter > 1);  // 检查是否完整读取最后一个字符
}

📌 关键原理总结

  1. 变长编码:UTF-8 使用 1-4 字节动态编码 Unicode
  2. 首字节标识:高位 1 的数量表示总字节数
  3. 续字节格式:后续字节必须以 10 开头
  4. 内存安全:通过 IsBadReadPtr 防止非法内存访问
  5. 边界校验:严格验证多字节序列完整性

流程图使用 Mermaid 语法绘制,可在支持 Mermaid 的环境(如Obsidian、VSCode插件)中查看动态效果。

http://www.dtcms.com/a/340879.html

相关文章:

  • 【Day 30】Linux-SQL语句
  • C/C++ 与嵌入式岗位常见笔试题详解
  • MYSQL为什么会发生死锁,怎么解决
  • 第三阶段数据-3:数据库脚本生成,备份与还原,分离与附加
  • configtx通道配置文件
  • RHCA08内存管理
  • 对称加密算法
  • 数据库DML语言(增、删、改)
  • 闪电赋能全链路:领码SPARK一体化创新平台
  • 基于HTTP3的WebTransport实践
  • 基于 Java 和 MySQL 的精品课程网站
  • 在完全没有无线网络(Wi-Fi)和移动网络(蜂窝数据)的环境下,使用安卓平板,通过USB数据线(而不是Wi-Fi)来控制电脑(版本2)
  • Ubuntu 重连usb设备(断电和不断电方案)亲测可行
  • 亚马逊新品爆单策略:从传统困境到智能突破
  • LeetCode热题100--101. 对称二叉树--简单
  • C++ 力扣 438.找到字符串中所有字母异位词 题解 优选算法 滑动窗口 每日一题
  • 《数据之舞》
  • GitHub宕机生存指南:从应急协作到高可用架构设计
  • QT-图像灰度处理时QImage.setPixel方法存在的坑
  • 在QT中动态生成控件造成界面卡顿时的鼠标处理
  • Qt设置软件使用期限【新版防修改系统时间】
  • 一个 WPF 文档和工具窗口布局容器
  • GitHub宕机应急指南:无缝协作方案
  • Eclipse 里Mybatis的xml的头部报错
  • 软考高级--系统架构设计师--案例分析真题解析
  • Java项目基本流程(五)
  • DeepSeek API 申请与 Node.js 对接指南
  • 服务器硬件电路设计之 SPI 问答(一):解密 SPI—— 从定义到核心特性
  • 服务器硬件电路设计之 SPI 问答(三):SPI 信号完整性守护与时钟频率的硬件设计羁绊
  • PCL+Spigot服务器+python进行MC编程2(使用RCON)---可以生成角色