学习Windows编程的时候,遇到字符串处理会让人非常抓狂,当然问题的根本还是自己学艺不精,不过还是得吐槽一下,造成这一局面的原因是规则变化多端而又有点不可捉摸,这不,最近就掉到坑里面去了。
先看看下面的这段代码:
int main(int argn,char* argv[])
{
char strA[]="ABC 简体中文";
wchar_t strW[]=L"ABC 简体中文";
printf("%s\n",strA);
wprintf(L"%s\n",strW);
return 0;
}
猜猜看,输出是什么?在我的电脑上(程序使用VS2010编译通过,Windows 7操作系统,简体中文版)运行的结果是这样的:
第一个还好好地,怎么第二个会出现三个问号?
调试一下试试看,在return 0前面下断点,然后查看内存:
这个是strA在内存中的值:41 42 43 20 bc f2 cc e5 d6 d0 ce c4 00
而strW则是:41 00 42 00 43 00 20 00 80 7b 53 4f 2d 4e 87 65 00 00
首先,我们知道char类型占一个字节,而wchar_t则是占两个字节,前面的41,42,43,20就是分别’A’,’B’,’C’和’ ‘(空格),这里表明,宽字符采用的是Little-Endian方式存放两个字节的,接下来我们把重点都放在汉字上面。
在strA中,表示汉字“简体中文”数据为bc f2 cc e5 d6 d0 ce c4,而在strW则是80 7b 53 4f 2d 4e 87 65,差别很大,为什么是这样呢?
这其中涉及到了编码的问题。char类型中出现的汉字,采用的是GB2312的编码规则,查询该编码表,可以发现,“简”字的编码为BCF2,“体”为CCE5,“中”为D6D0,而“文”则为CEC4,这就是strA中中文的表示方式,但是在宽字符strW中,采用的编码则是Unicode编码,在Windows平台下Unicode编码值就是UTF-16编码值。查询“简体中文”四个汉字的编码,可以发现是7B80 4F53 4E2D 6587,由于计算机的架构为Little-Endian,需要把高低位字节互换,这也就是宽字符的表示形式。
根据网页上的说明(参考这里)C/C++标准只是声明wchar_t是一个可以表示字符集中的任意一个字符的足够宽的变量类型。wchar_t可以用任何encoding编码方式来存储这个字符,如ANSI、UCS-2或者UCS-4, 甚至是SCU-128,只不过我们通常是用unicode编码方式。wchar_t是与实现相关的。所以为了可移植性,我们不能假定wchar_t的编码方式,然后根据编码方式做一些相关性操作,我们只能理解它为一个足够宽的字符类型。
最后,我们还能顺便发现,wprintf函数在处理文本输出的时候,并不处理编码问题,而是直接按多字节字符顺序输出。
好,现在问题的原因找到了,那么该如何解决问题呢?
Windows当然不会没有想到这个问题,在Windows中,提供了如下两个函数:WideCharToMultiByte和MultiByteToWideChar,他们都位于头文件winnls.h中,分别是将宽字符转化为多字节和将多字节转化为宽字符。下面的例子直接给出了转化的代码,函数的具体使用方法可以翻阅MSDN。
int main(int argn,char* argv[]) { wchar_t strW[]=L"ABC 简体中文"; char* pW2A; int t=0; //第一次,确定需要的字节数 t=WideCharToMultiByte(CP_ACP,0,strW,-1,NULL,0,NULL,FALSE); if(t!=0) { pW2A=(char*)malloc(t);//分配内存,然后运行第二次,注意参数区别 WideCharToMultiByte(CP_ACP,0,strW,-1,pW2A,t,NULL,FALSE); printf("%s\n",pW2A); free(pW2A); } return 0; }
这回显示就没有什么问题了,长舒一口气,暂时从坑里面爬出来了。