宽字符和窄字符的一个坑

学习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操作系统,简体中文版)运行的结果是这样的:

image

第一个还好好地,怎么第二个会出现三个问号?

调试一下试试看,在return 0前面下断点,然后查看内存:

image

image

这个是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;
}
这回显示就没有什么问题了,长舒一口气,暂时从坑里面爬出来了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值