现在的位置: 首页 > 综合 > 正文

关于字符编码以及%ls wsprintf setlocale 资料整理

2013年07月06日 ⁄ 综合 ⁄ 共 2498字 ⁄ 字号 评论关闭

     这两天研究数据库,老是遇到宽字符的问题:

     
  •      问题一:为什么在用wsprintf输出Unicode编码的字符串时要先调用setlocale(LC_ALL, "chs");而对于printf输出多字节编码的字符串则有无setlocale均可正常输出。   如:

  

void main()
{
     wchar_t* wstr = L"中文";
     setlocale(LC_ALL, "chs");
     wprintf(L"%ls", wstr);
}
得到输出:
void main()
{
     char* wstr = "中文";
     setlocale(LC_ALL, "chs"); //可要可不要
     printf("%s", wstr);
}
得到输出:
  •      问题二: %ls的作用,对比下面四种在VS2008下的小代码,它们分别将printf wprinf %s %ls组合,用于输出Unicode字符串,为了比较的针对性,我们在每个程序中均加入setlocale(LC_ALL, "chs");
void main()
{
     wchar_t* wstr = L"中文";
     setlocale(LC_ALL, "chs");
     printf("%s", wstr);
}

输出:

void main()
{
     wchar_t* wstr = L"中文";
     setlocale(LC_ALL, "chs");
     printf("%ls", wstr);
}

输出:

void main()
{
     wchar_t* wstr = L"中文";
     setlocale(LC_ALL, "chs");
     wprintf(L"%s", wstr);
}

输出:

void main()
{
     wchar_t* wstr = L"中文";
     setlocale(LC_ALL, "chs");
     wprintf(L"%ls", wstr);
}

输出:

按照网上一篇博客的说法,%ls是将后面的字符串按照Unicode编码的方式解释,但是为什么对于wprintf来说,%s和%ls却没有区别。


     之后找了一上午的资料,到现在解决了大部分的困惑,因此整理一下收集的资料,对于引用部分,均直接使用能找到的原创出处。

     
     关于字符编码,C和Unicode之间的问题引进:
     这里所说的C和Unicode之间的问题,就是标准C并不支持Unicode,这也是前面我遇到的问题一的关键所在,有了这一点认识,再结合一段帖子回复(原帖网址: http://bbs.chinaunix.net/thread-3693579-1-1.html):
     这个问题涉及到一个字符,他在源代码时是以什么形式(或者说编码格式)存的,在编译好的二进制文件中是以什么形式存的,以及最后输出的时候输出的是什么编码格式。


如果是普通字符串,那么它在这三者中表现形式是一致的。而宽字符串,却有可能不同。

以linux为例,因为linux下通常使用的字符编码都是utf8,所以源码也是以utf8保存的,对于普通字符串,在编译器编译的过程中,什么也不做,原样将这个编码放到二进制文件中。然后printf输出的时候,也是原样输出。如果接收输出的那个程序(也许是一个shell)支持utf8,那么当然就可以正常显示出来了。如果不支持,就会错乱。

而对于宽字符来说,还以linux为例。源码中依然是utf8,但编译器在编译过程中,会把字符的编码转换成unicode保存在二进制文件中。而输出的形式,取决于你的locale设定了。如果shell支持的是utf8,但你设定的locale是gbk,printf的时候程序就会把unicode转成gbk编码输出,而这边shell却当成utf8编码解释,最后当然就乱码了。

     
     有了上面的解释,相信问题一已经迎刃而解了。顺便再补充一些小点:
  1.      vs2008下的字符集(项目--项目属性--配置属性--常规)为Unicode只能说明编译器预先宏定义了_UNICODE宏,这个宏使得编译器在编译头文件时,能够选择适当的函数和类型,如如果宏定义了_UNICODE,那么CString将被宏替换为CStringw,_tprintf将替换为wprintf,MessageBox替换为MessageBoxW,事实上在windows提供的DLL中是并没有诸如MessageBox这样的函数名的。而宏定义了UNICODE并不是代码中出现的字符串都是Unicode编码的,要使用Unicode编码的字符串常量,需要在前面加上L,如L"中文abc"。事实上,如前面那段帖子回复阐述的那样,在代码中出现的任何字符串都是以当前系统使用的字符编码来保存的(在VS下好像是chs),只有当编译器编译时,编译器才将前面有L标识的字符串常量通过mbstowcs()转换为Unicode编码的字符串保存在二进制文件(PE文件)中。
  2.       无论是UTF-8 CHS_GB2312 等都是多字节编码的一种,正因为有了这么多的字符编码方式,所以我们在shell中输出时,由于编译器要调用wcstombs()将Unicode转换为多字节编码,但是编译器并不知道当前shell使用的那种编码,所以才需要setlocale来说明。

        关于字符编码和setlocale更多资料参见:

        http://blog.csdn.net/dawei_sun/article/details/3541351 关于字符编码在c++标准IO和文件处理中遇到的问题
     关于问题二,在网上看到并不是%s和%ls对wprintf没有影响,而是在Windows下如此,在Linux环境下,wprintf(L"%s", L"中文");是不能得到正确结果的。主要参见资料有:

抱歉!评论已关闭.