Unicode编码的四字节字符的判断方法

项目场景:

在MFC静态文本控件上显示一串字符串,用wchar_t数组存储。如果是连续的半角英文字母或者数字,不能自动换行(汉字或字符串中有空格的话也可以自动换行),后面的字符会被隐藏掉。为了让显示的字符串都能根据文本控件的长度而自动换行,根据控件的长度在字符串的适当位置添加换行符,实现字符串的自动换行。

在这里插入图片描述


问题描述

字符串中有四字节字符的时候,字符有可能显示异常


原因分析:

在Windows系统下,wchar_t采用UTF-16编码。对于英文和大部分中文,它们通常占用2个字节,用于存储Unicode编码的字符。然而,对于某些特殊的字符,它们可能需要占用4个字节(比如𩸽),需要2个wchar_t来存储。

所以如果字符串中有四字节字符的时候,按上述方法,有可能在存储4字节的2个wchar_t中间添加换行符,导致这个字符显示异常。

验证

俩个一样大小的文本控件,左边的控件一行正常可以显示8个汉字。更改显示到右边控件的字符串,在第存储第8个汉字的俩个wchar_t中间,添加换行符。结果和猜想一致,第8个汉字显示异常。

wchar_t wchLeftText[100];
memset(wchLeftText, 0, sizeof(wchLeftText));

wchar_t wchRightText[120];
memset(wchRightText, 0, sizeof(wchRightText));

wsprintf(wchLeftText, L"%s", L"𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽𩸽");

int iTemp = 0;
for (int i = 0; i < 100; i++)
{
	wchRightText[iTemp] = wchLeftText[i];
	iTemp++;
	if (i == 14)
	{
		wchRightText[iTemp] = '\n';
		iTemp++;
	}
}
GetDlgItem(IDC_STATIC)->SetWindowText(wchLeftText);
GetDlgItem(IDC_STATIC1)->SetWindowText(wchRightText);

在这里插入图片描述


解决方案:

判断每个wchar_t存储的内容是2字节字符还是4字节字符,如果是4字节字符,换行符不能添加在4字节字符中间。

4字节字符的判断方法
字符的前16位范围是0xD800~0xDBFF

int utf16CodePoint = (int)wchar_t[x]
if (utf16CodePoint >= 0xD800 && utf16CodePoint <= 0xDBFF)
{
    // 4字节
}

[参考]

UTF-16是一种用于表示Unicode字符的编码方式,它使用16位(2字节)来表示一个字符。而UCS-2则是一种更早的字符编码方式,它使用16位(2字节)来表示一个字符,但只能表示BMP(Basic
Multilingual Plane,基本多语言平面)中的字符。

为了向后兼容UCS-2,UTF-16使用了一种称为surrogate pair的机制。Surrogate
pair由两个相邻的字符组成,一个称为high surrogate(高代理),另一个称为low
surrogate(低代理)。它们一起表示一个超出BMP范围的字符。

具体来说,高代理的范围是0xD800到0xDBFF,而低代理的范围是0xDC00到0xDFFF。每个高代理都与一个低代理配对,共同表示一个4字节的字符。这样做的目的是为了将UCS-2中的字符映射到UTF-16中,以便在保持向后兼容的同时,能够表示更多的字符。

通过使用surrogate
pair,UTF-16能够表示的字符范围是0x000000到0x10FFFF,这与UCS-2中的字符范围相同。然而,UTF-16能够表示的字符数量实际上超过了UCS-2的范围。这是因为UCS-2只能表示2字节(16位)的字符,而UTF-16可以使用最多4字节(32位)来表示一个字符。

总之,surrogate
pair是一种用于UTF-16编码中表示超出BMP范围的字符的机制。它通过将高代理和低代理配对起来,共同表示一个4字节的字符,实现了UTF-16对UCS-2的向后兼容。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在STM32上实现Unicode编码转中文需要使用UTF-8编码的转换算法,具体步骤如下: 1. 将Unicode编码分成1-4个字节,每个字节的最高位都是1,其余7位用于存储Unicode编码的二进制值。 2. 根据UTF-8编码规则将Unicode编码转换成UTF-8编码,例如对于单字节Unicode编码,其UTF-8编码与其二进制值相同;对于多字节Unicode编码,其UTF-8编码由多个字节组成,第一个字节的最高位为1,其余7位用于标识该Unicode编码需要几个字节来表示,后面的字节的最高两位均为10,其余6位用于存储Unicode编码的二进制值。 3. 将UTF-8编码转换成中文字符方法是根据UTF-8编码规则还原出Unicode编码,然后查找Unicode编码对应的中文字符。 以下是一个简单的代码示例: ```c #include <string.h> #include <stdio.h> // Unicode编码转换成UTF-8编码 void unicode2utf8(uint16_t unicode, uint8_t* utf8) { if (unicode <= 0x7F) // 单字节编码 { utf8[0] = unicode & 0x7F; utf8[1] = 0; } else if (unicode <= 0x7FF) // 双字节编码 { utf8[0] = 0xC0 | ((unicode >> 6) & 0x1F); utf8[1] = 0x80 | (unicode & 0x3F); utf8[2] = 0; } else if (unicode <= 0xFFFF) // 三字节编码 { utf8[0] = 0xE0 | ((unicode >> 12) & 0x0F); utf8[1] = 0x80 | ((unicode >> 6) & 0x3F); utf8[2] = 0x80 | (unicode & 0x3F); utf8[3] = 0; } else // 四字节编码 { utf8[0] = 0xF0 | ((unicode >> 18) & 0x07); utf8[1] = 0x80 | ((unicode >> 12) & 0x3F); utf8[2] = 0x80 | ((unicode >> 6) & 0x3F); utf8[3] = 0x80 | (unicode & 0x3F); utf8[4] = 0; } } // UTF-8编码转换成Unicode编码 uint16_t utf82unicode(uint8_t* utf8) { uint16_t unicode = 0; uint8_t len = strlen((const char*)utf8); if ((utf8[0] & 0x80) == 0) // 单字节编码 { unicode = utf8[0]; } else if ((utf8[0] & 0xE0) == 0xC0 && len >= 2) // 双字节编码 { unicode = ((utf8[0] & 0x1F) << 6) | (utf8[1] & 0x3F); } else if ((utf8[0] & 0xF0) == 0xE0 && len >= 3) // 三字节编码 { unicode = ((utf8[0] & 0x0F) << 12) | ((utf8[1] & 0x3F) << 6) | (utf8[2] & 0x3F); } else if ((utf8[0] & 0xF8) == 0xF0 && len >= 4) // 四字节编码 { unicode = ((utf8[0] & 0x07) << 18) | ((utf8[1] & 0x3F) << 12) | ((utf8[2] & 0x3F) << 6) | (utf8[3] & 0x3F); } return unicode; } // UTF-8编码转换成中文字符 void utf82chinese(uint8_t* utf8, uint8_t* chinese) { uint16_t unicode = utf82unicode(utf8); // 根据Unicode编码查找对应的中文字符,这里使用一个简单的查表方式 switch (unicode) { case 0x4E00: strcpy((char*)chinese, "一"); break; case 0x4E01: strcpy((char*)chinese, "丁"); break; case 0x4E02: strcpy((char*)chinese, "丂"); break; // ... default: strcpy((char*)chinese, ""); break; } } int main() { uint16_t unicode = 0x4E00; // 中文字符"一"的Unicode编码 uint8_t utf8[5] = {0}; uint8_t chinese[3] = {0}; unicode2utf8(unicode, utf8); printf("UTF-8编码:"); for (int i = 0; utf8[i] != 0; i++) { printf("%02X ", utf8[i]); } printf("\n"); utf82chinese(utf8, chinese); printf("中文字符:%s\n", chinese); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值