如何将int转换为宽字符?解析sprintf多字节编码异常问题
关于sprintf用%lc格式化宽字符的编码问题
当我使用sprintf配合%lc格式化大于255的整数时,sprintf会将单个宽字符保存为多个单字节字符。
示例代码如下:
#include <locale.h> #include <stdio.h> int main() { setlocale(LC_ALL, ""); char buf[200] = "\0"; int wide_char = 500; // example sprintf(buf, "%lc test", wide_char); printf("%s\n", buf); printf("%d %d %d\n", (unsigned char)buf[0], (unsigned char)buf[1], (unsigned char)buf[2]); return 0; }
运行结果:
Ǵ test 199 180 32
值为500的宽字符被转换为199和180,对应的二进制格式如下:
1 11110100 // 500 11000111 10110100 // 199和180
我的目标是理解如何将int转换为宽字符,并学会手动将宽字符保存到字符串中(如同处理普通字符那样,比如buf[0] = 97)。我观察到一些规律,但无法理解。更多示例:
10000000 // int = 128 11000010 10000000 // 编码后的宽字符 1 10000001 // int = 385 11000110 10000001 10 10000001 // int = 641 11001010 10000001 11 10000001 // int = 897 11001110 10000001 1 00000110 // int = 262 11000100 10000110
编码规则解释
你看到的是UTF-8编码的转换逻辑,setlocale(LC_ALL, "")会将程序的locale设置为系统默认编码(通常是UTF-8),sprintf用%lc处理宽字符时,会把你传入的int值(即Unicode码点)转换成UTF-8编码的多字节序列。
针对你示例里的码点范围(128~2047),UTF-8的2字节编码规则如下:
- 将码点转为二进制,补前导零凑成11位。比如500的二进制是
11110100,凑成11位为001 1110100。 - 第一个字节格式为
110xxxxx:取11位里的前5位填入xxxxx,即110+00111→11000111(对应十进制199)。 - 第二个字节格式为
10xxxxxx:取11位里剩下的6位填入xxxxxx,即10+110100→10110100(对应十进制180)。
所有示例都符合这个逻辑,比如码点128的二进制10000000凑成11位是000 1000000,第一个字节是11000010,第二个字节是10000000,和示例完全匹配。
手动保存宽字符到字符串的方法
如果要手动把Unicode码点(int值)转成UTF-8并存入char数组,直接按上述规则拆分二进制即可:
- 判断码点范围,确定需要的字节数(128~2047用2字节)。
- 按UTF-8的格式填充每个字节的二进制位。
- 将每个字节的二进制转为unsigned char,依次存入数组。
比如手动处理码点500:
char buf[3]; // 第一个字节:0xC7对应二进制11000111 buf[0] = 0xC7; // 第二个字节:0xB4对应二进制10110100 buf[1] = 0xB4; // 字符串结束符 buf[2] = '\0';
此时buf中就是UTF-8编码的Ǵ字符。
内容的提问来源于stack exchange,提问作者user567
相关产品推荐
相关产品推荐

