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 字符编码结构
字节数 首字节范围 编码模板
1 0x00-0x7F 0xxxxxxx
2 0xC0-0xDF 110xxxxx 10xxxxxx
3 0xE0-0xEF 1110xxxx 10xxxxxx 10xxxxxx
4 0xF0-0xF7 11110xxx 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++ 实现代码 (带详细注释)

cpp 复制代码
#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# 实现说明 (优化版)

csharp 复制代码
// 导入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插件)中查看动态效果。

相关推荐
重启的码农3 小时前
llama.cpp 分布式推理介绍(5) RPC 通信协议
c++·人工智能·神经网络
重启的码农3 小时前
llama.cpp 分布式推理介绍(6) 张量序列化 (rpc_tensor)
c++·人工智能·神经网络
让我们一起加油好吗3 小时前
【C++】模板(进阶)
c++·visualstudio·模板·泛型编程
雨落倾城夏未凉3 小时前
2.如何创建自己的类
c++·后端
Ustinian_3105 小时前
【C/C++】For 循环展开与性能优化【附代码讲解】
c语言·开发语言·c++
MZ_ZXD00110 小时前
springboot汽车租赁服务管理系统-计算机毕业设计源码58196
java·c++·spring boot·python·django·flask·php
岁忧12 小时前
(nice!!!)(LeetCode 每日一题) 679. 24 点游戏 (深度优先搜索)
java·c++·leetcode·游戏·go·深度优先
小欣加油12 小时前
leetcode 3 无重复字符的最长子串
c++·算法·leetcode
zylyehuo15 小时前
C++基础编程
c++