本例介绍了C语言使用的两种字符集, 多字节编码字符集(GBK, UTF-8等)和UNICODE(UCS-2)字符集以及它们的区别。
从程序中我们可以学习到, 除过早期C语言支持的ASCII编码外, 新的C语言还支持多字节编码和UNICODE编码, 后两者都这是一种可以包含国际化文字的编码格式, 而且从Windows2000之后系统内核统一采用UNICODE文字编码格式。
C语言同时支持多字节编码和UNICODE编码, 所以对应的数据类型也就提供了两个, char类型和wchar_t类型。对应的字符、字符串操作函数也同时提供了两套, 普通的C标准字符串函数库和以w开头的UNICODE版本扩展函数库(注意, 原有以str开头的字符串函数, 其UNICODE版本是以wcs开头的)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
// 定义缓冲区长度为256个字符
// (注意, 这里为什么用"字符"而不是"字节")
#define BUFFER_LEN 256
int main(int argc, char* argv[])
{
// 定义一个ASCII字符变量
char ce = 'A';
// 定义一个UNICODE字符变量
wchar_t wce = 'A';
// 定义一个ASCII字符串变量, 包含一个汉字
// (由于一个汉字占用2字节(GB10080编码)),
// 所以一个char类型变量无法存储, 需要一个字符串来存储,
// 所以实际上这个“大”字占据了3个字节, 两个字节存放字符
// 本身编码, 还有一个/0结束符
char szC[] = "大";
// 定义一个UNICODE字符变量, wchar_t变量可以存放一个汉
// 字, 注意字符前的大写L, 这个符号表示该字符为UNICODE字
// 符集字符
wchar_t wcC = L'大';
// 定义ASCII字符集字符串变量
const char cszHello[] = "Hello";
// 定义UNICODE字符集字符串变量, 注意字符串前的大写L,
// 这个符号表示该字符串为UNICODE字符集字符串
const wchar_t cwszHello[] = L"Hello";
/*********************************************************
* 学习字符集, 一开始要搞清楚不同字符集占据的空间大小。
* ASCII字符集每个字符占据1字节, 使用char表示
* UNICODE字符集每个字符占据2字节, 使用wchar_t(部分
* 版本C语言使用unsigned short)表示
*********************************************************/
// 定义ASCII字符集字符串指针
// (思考一下, pcszHello和cszHello这两个变量定义的区别在
// 哪里, 它们各自代表了什么?)
const char* pcszHello = "大家好";
// 定义UNICODE字符集字符串指针
const wchar_t* pcwszHello = L"大家好";
// 定义BUFFER_LEN长度存放字节的缓冲区
// (定义的同时初始化缓冲区是一个好习惯)
char szBuffer[BUFFER_LEN] = "";
// 定义BUFFER_LEN长度存放UNICODE字符的缓冲区
wchar_t wszBuffer[BUFFER_LEN];
// 使用memset函数可以初始化任何数组, 包括字符串数组
memset(wszBuffer, 0, sizeof(wszBuffer));
// 在最新的C语言标准中, 所有UNICODE字符在显示前需要
// 设置其国家代码(或称为地域信息), 这里设置为中国
// LC_ALL表示设置所有相关项目为中国, 包括文字、时间和
// 货币
_wsetlocale(LC_ALL, L"zhi");
// 输出ASCII英文字符
printf("size of %c is %d, code is: %u", ce, sizeof(ce), (int)ce);
// 输出UNICODE英文字符
wprintf(L"/nsizeof %c is %d, code is: %u", wce, sizeof(wce), (int)wce);
/*********************************************************
* 通过上述代码可以发现, 对于英文字符, ASCII编码和
* UNICODE编码的内码相同, 但占用空间不同
*********************************************************/
// 输出ASCII中文字符(实际是一个字符串)
printf("/nsize of %s is %d, code is: %u", szC, sizeof(szC), (int)*(unsigned short*)szC);
// 输出UNICODE中文字符
wprintf(L"/nsizeof %c is %d, code is: %u", wcC, sizeof(wcC), (int)wcC);
/*********************************************************
* 通过上述代码可以发现, GB10080编码和UNICODE编码在
* 编码“大”字时, 编码值是不同的, 但都占据2字节空间
*********************************************************/
// 使用printf函数输出ASCII字符集字符串并输出其占据空间的字节数
printf("/nsize of %s is %d", cszHello, sizeof(cszHello));
// 使用wprintf函数输出UNICODE字符集字符串并输出其占据空间的字节数
wprintf(L"/nsize of %s is %d", cwszHello, sizeof(cwszHello));
/*********************************************************
* 通过上述的练习可以发现:
* ASCII字符集字符串长度和其占用空间的字节数一致
* (包括结束符/0, 占据1 byte)
* UNICODE字符集字符串长度是其占用空间字节数的2倍
* (包括结束符/0, 占据2 byte), 这一点和wchar_t类型为2字
* 节一致
*********************************************************/
// 使用strlen函数测量ASCII字符串长度
printf("/nlength of %s is %d", pcszHello, strlen(pcszHello));
// 使用wcslen函数测量UNICODE字符串长度
wprintf(L"/nlength of %s is %d", pcwszHello, wcslen(pcwszHello));
// 使用strcpy_s函数复制ASCII字符串(后缀为_s的函数是原函数的"安全版本",
// 改进了可能出现缓冲区溢出问题的漏洞)
strcpy_s(szBuffer, BUFFER_LEN, cszHello);
// 使用strcat_s函数连接ASCII字符串
strcat_s(szBuffer, BUFFER_LEN, pcszHello);
printf("/nlength of %s is %d", szBuffer, strlen(szBuffer));
// 使用wcscpy_s函数复制UNICODE字符串
wcscpy_s(wszBuffer, BUFFER_LEN, cwszHello);
// 使用wcscat_s函数连接UNICODE字符串
wcscat_s(wszBuffer, BUFFER_LEN, pcwszHello);
wprintf(L"/nlength of %s is %d", wszBuffer, wcslen(wszBuffer));
wprintf(L"/n");
system("pause");
return 0;
}