在使用VS编程时,在项目设置中有一个关于字符集的选项。一是Unicode字符集(VS默认的字符集),二是多字节字符集。本文围绕这两个字符集做一简单介绍。
一、先说一下多字节字符集
最早的字符集是ANSI的ASCII字符集,它开始使用7位后来使用8位表示包括英文字母、数字、标点符号、制表符、控制符等共计256个字符。后来,随着各国在ASCII的基础上制定本国的字符集,这些从ANSI标准派生的字符集被习惯的统称为ANSI字符集或者MBCS(多字节字符集)。例如:汉字GB-2312编码就是使用ASCII字符集中大于128的字符两两组合表示一个汉字的。
二、介绍一下Unicode
将不同国家各自的字符编码方式,一步一步进行统一形成了Unicode 编码。Unicode 为每个字符提供一个号码,称为码点,它几乎囊括了世界上所有文字的字符。Unicode 编码一共包含了100多万个字符,码点范围是 U+0000 到 U+10FFFF(16进制0000至10FFFF)。Unicode编码可以认为是一张纸上的编码表,具体在计算机中怎样实现呢?Unicode提供了三种计算机编码方法:
a.UTF-8(使用1个到4个字节表示一个码点);
b.UTF-16(使用2个字节或4个字节表示一个码点);
c.UTF-32(使用4个字节表示一个码点)。
三种编码方式中使用最为广泛的是UTF-8,VS中默认的“使用Unicode字符集”就是UTF-8。在UTF8中表示英语字符的编码方法与ASCII(ANSI)编码相同。
说到这里,上述两种字符集给人的感觉是殊途同归。实际上只是在英文字符方面一样,涉及到中文等字符,问题就显示出来了。
三、char*类型处理汉字的存在的问题
举个例子看一下:
在项目设置上无论是选择Unicode字符集还是选择多字符集都可以,运行下面的代码:
char s[] = "中国";
char s1[] = "china";
printf("s=%d\n", sizeof(s));//运行结果:s=5
printf("s1=%d\n", sizeof(s1));//运行结果:s1=6
"中国"这2个汉字的字节长度是5(1个汉字2个字节再加上1个\0),英文"china"的字节长度是6(5个字母加1个\0)。而要显示"中"字需要语句:printf("%c%c",s[0],s[1])才能把"中"显示出来,也就是需要两个数组成员才能表示出来;而显示英文字符"c",printf("%c",s1[0])就可以,如此就给处理中英文混合的字符串带来了困难。其原因是英语字符是单字节字符,而汉字是双字节字符。还有一些国家的字符是三字节的。一旦字符串里面包含多字节字符,就意味着字符串的字节数与字符数不再一一对应了。C语言中的很多char*类型字符串函数都是针对单字节字符的,如用toupper()、tolower()、isalpha()等处理汉字都不会得到正确结果。
三、宽字符
宽字符就是每个字符占用相同的字节数(2个或4个字节)。宽字符有一个单独的数据类型wchar_t,每个宽字符都是这个类型。该类型的长度为2个字节或4个字节,具体是2还是4取决于使用的系统。宽字符的定义在头文件wchar.h中。使用宽字符的字面量前面必须加上前缀“L”,否则会报错。当使用宽字符时,不论英语字符还是汉字字符,包括结束符"\0"都占2个字符(Win10 VS2022系统),这样处理起字符串来就方便多了,字节数除以2就是字符数。当然,宽字符下,有些函数有变化,比如frintf变成了wfrintf,使用方法包括占位符也有变化。使用宽字符类型相关函数还要使用setlocale设置环境,这些函数都定义在wchar.h中。这些函数在学习头文件内容时再做介绍。
四、宽字符与多字节字符转换函数
1、mblen()函数
函数mblen返回一个多字节字符占用的字符数,它的原型定义在头文件stdlib.h中。
使用格式:mblen(参1,参2);
参1:多字节字符串指针(实际检查的就是第一个字符)
参2:需要检查的最大字节数(固定使用宏MB_CUR_MAX)
返回值:一个多字节字符占用的字符数(注意:是字符数,不是字节数,返回值为int类型);如果字符是空的宽字符则返回0;字符不是有效的多字节字符返回-1。
2、wctomb()函数
函数wctomb用于将宽字符转为多字节字符,原型定义在头文件stdlib.h中。
使用格式:wctomb(参1,参2);
参1:接收转换后多字节字符数组指针
参2:被转换的宽字符(wchar_t类型)
返回值:返回多字节字符存储占用的字节数量;如转换失败则返回-1 。
说明:
a.转换前的宽字符一个字符就是一个字符,转成char*类型后,需要两个或多个字符表示。
b.调用一次函数只能处理一个宽字符。
c.注意使用该函数需要setlocale设置环境。
3、mbtowc()函数
函数mbtowc功能是将多字节字符转为宽字符,其原型定义在头文件stdlib.h中。
使用格式:mbtowc(参1,参2,参3);
参1:接收转换结果的宽字符指针
参2:待转换的多字节字符指针
参3:多字节字符的字节数
返回值:返回多字节字符的字节数;转换失败返回-1。
4、wcstombs()函数
函数wcstombs的功能是将宽字符串转换为多字节字符串,其原型定义在头文件stdlib.h中。
使用格式:wcstombs(参1,参2,参3);
参1:接收转换结果的多字节字符串指针
参2:等待转换的宽字符串指针
参3:存储多字节字符串的最大字节数
返回值:转换成功返回是转换后的多字节字符串的字节数(不含\0符);转换失败返回-1。
5、mbstowcs()函数
函数mbstowcs功能是将多字节字符串转换为宽字符串,其函数原型定义在头文件stdlib.h中。
使用格式:mbstowcs(参1,参2,参3);
参1:接收转换结果的宽字符串指针
参2:等待转换的多字节字符串指针
参3:待转换的多字节字符串的最大字符数
返回值:转换成功返回多字节字符的数量;转换失败时返回-1。
五、举例程序如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include<wchar.h> //宽字符
#include<locale.h> //宽字符环境设置
int main(void)
{
//汉字由两个大于127的ASCII符组成
unsigned char ch[] = "中";
printf("ch[]的字节长度:%d\n", (int)sizeof(ch));//运行结果:3
printf("中的ASCII码值:%d %d\n", ch[0], ch[1]);//运行结果:214 208
strcpy(ch,"国");
printf("国的ASCII码值:%d %d\n", ch[0], ch[1]); //运行结果:185 250
printf("ASCII码185 250转成汉字:%c%c\n", 185, 250); //运行结果:国
//宽字符(本机环境每个字符两个字节,不区分英文、汉字等)
setlocale(LC_ALL, ""); //宽字符环境设置
printf("wchar_t的sizeof=%d\n", (int)sizeof(wchar_t));//运行结果:wchar_t的sizeof=2
wchar_t s[] = L"中国china"; //连汉字带英文加上结束符共计8个字符
printf("s=%d\n", (int)sizeof(s));//运行结果:s=16 (16/2=8)
wchar_t s1[] = L"天津市(Tianjin City)建城600多年了!"; //汉字括弧字母数字标点结束符总计27个字符
printf("s=%d\n", (int)sizeof(s1));//运行结果:s1=54(54/2=27)
wprintf(L"%ls\n", s); //宽字符显示函数 运行结果:中国china
//函数mblen的使用
printf("char数组ch[]汉字“中”占用字节数:%d\n", mblen(ch, MB_CUR_MAX)); //运行结果:2
//char*类型中,需要两个字符表示一个汉字(具体结果是否是2还与环境设置相关)
printf("wchar_t数组s[]汉字“中”占用字节数:%d\n", mblen(s, MB_CUR_MAX)); //运行结果:1
//宽字符类型中,一个汉字及时一个字符,一个英文字母也是一个字符
//函数wctomb的使用
wchar_t wc = L'天';
char c[6] = {0};
int ret = wctomb(c,wc);
printf("转换后占%d字节:%s\n", ret, c);//运行结果:转换后占2字节:天
//函数mbtowc的使用
char c1[]="津";
wchar_t wc1[5] = {0};
mbtowc(wc1,c1,sizeof(c1));
wprintf(L"char*转成wchar_t:%ls\n", wc1); //运行结果:char*转成wchar_t:津
//函数wcstombs的使用
char c2[24] = { 0 };
wchar_t wc2[] = L"计算机ABC";
wcstombs(c2,wc2,sizeof(c2));
printf("转换后:%s\n", c2);
//函数mbstowcs的使用
char c3[] = "123数字abc字母";
wchar_t wc3[100];
mbstowcs(wc3, c3, sizeof(c3));
wprintf(L"转换后:%ls", wc3);
getchar();
return 0;
}