1. couthtml
场景1: 在源文件中定义 const char* str = "中文" 在 VC++ 编译器上,因为Windows环境用 GBK编码,因此字符串 "中文" 被保存为 GBK内码,
编译器也把 str 指向一个包含有 GBK编码的只读内存空间.
用 cout 输出 str 时, 因为中文Windows环境用GBK编码,因此把GBK编码的 str 内容输出到控制台,没问题.
场景2: 在Linux 下编辑一个文件 const char* str = "中文", 因为Linux广泛使用 UTF8 编码,因此在源文件里, "中文" 被保存为 UTF8内码.
而后在Windows中打开这个源文件,因为Windows使用GBK编码,因此VC++ 按照GBK去解释被保存为 UTF8 内码的 "中文", 显示为乱码.
2. wcout
在源文件中定义 const wchar_t* str = L"中文" 在 VC++ 编译器上,因为指定了L,因此字符串 "中文" 被保存为UNICODE内码(UCS2),编译器也把 str 指向一个包含有 UNICODE 编码的只读内存空间.
用 wcout 输出 str 时, wcout 首先调用 wcstomb_s() (即根据当前 local 转换, 若是没有设置local,则是经典的C local, 不认识中文)把 str 的内容转换后交给控制台,结果天然什么都不显示. (调试代码能够知道VC++ 2010 实现是一个字符一个字符输出,调用 wctomb_s)
原理
咱们知道 cout 和 wcout 分别是 basic_ostream 的特化版本, 而 basic_ostream 调用 basic_streambuf 实际执行输出动做,针对 wchar_t,basic_streambuf有专门的特化函数,调用 fputwc 输出一个宽字符,而 fputwc 须要调用 wctomb_s 把宽字符转换后再输出. 咱们知道wctomb_s 是依赖 locale 的,因为默认状况下是C locale,因此用中文内码调用 wctomb_s 会失败.
解决办法
设置当前系统的locale 替代默认的 "C" locale, 使 wctomb_s 等函数能够正常工做.
如下3种方法中的任意一种均可以达到目的.
1. C函数设置全局locale
setlocale(LC_ALL, "");
2. C++ 设置全局locale
std::locale::global(std::locale(""));
2. 单独为 wcout 设置一个 locale
std::locale loc("");
std::wcout.imbue(loc);
结论
和Windows API 不一样 C++中的各类 w版本的类或者函数并不能提升性能,由于它们都须要用 wc..to..mb 之类的函数转换为ANSI兼容编码而后调用标准库函数.或者,若是库函数的实现者愿意,针对Windows系统,宽字符的fputwc能够直接调用UNICODE版本的Windows API而不用转换.可是这些都跟C++语言自己没有什么关系.因为Windows内核是UNICODE的,因此直接用 UNICODE 字符串调用 Windows API会有一点点好处.
C++设计者的出发点: 我无论你用什么字符编码,与C++无关,要输出时:若是是单字节字符或者多字节字符,直接输出;若是是宽字符,则根据local转换为多字节字符,而后再输出.
即便未来UNICODE过期了(假设,假设而已),也没关系,只要定义好新的local便可.对于C语言也是这样.ios
Windows设计者的出发点: 统一使用 Unicode 宽字符,解决一切问题函数
原文:http://blog.csdn.net/gonxi/article/details/5931006post
使用C++标准库的iostream,能够方便地将控制台、文件、字符串以及其它可扩充的外部表示做为流来处理,但要处理中文,却会碰到不少问题。本人原来没怎么用过这个iostream,这几天尝试用这个写点东西,一下子不能输出中文,一下子不支持中文文件名的,搞得头大。网上搜了搜,没有发现适用于全部状况的解决方案。不事后来本身通过屡次测试,基本解决了这些问题,如今写成文字做为一个总结,也供碰到一样问题的朋友参考。关于C语言中的 printf和wprintf的中文输出,本文也进行了探讨。
须要说明的是,个人开发环境是VS 2005(标准库固然也是微软实现的),不保证其它环境下是相同的效果。
一、cout和wcout
在缺省的C locale下,cout能够直接输出中文,但对于wcout却不行。对于wcout,须要将其locale设为本地语言才能输出中文:
wcout.imbue(locale(locale(),"",LC_CTYPE)); // ①
也有人用以下语句的,但这会改变wcout的全部locale设置,好比数字“1234”会输出为“1,234”。
wcout.imbue(locale(""));
二、ofstream和wofstream
在缺省的C locale下,ofstream能正确输出中文到文件中,但不支持中文文件名;wofstream支持中文文件名,但不能向文件中输出中文。要解决这个问题,须要在打开文件以前将全局locale设为本地语言。将全局locale设为本地语言后,ofstream和wofstream的问题都解决了,但 cout和wcout却不能输出中文了。要让cout和wcout输出中文,须要将全局locale恢复原来的设置,以下所示:
locale &loc=locale::global(locale(locale(),"",LC_CTYPE)); // ②
ofstream ofs("ofs测试.txt");
wofstream wofs(L"wofs测试.txt");
locale::global(loc); // ③
ofs<<"test测试"<<1234<<endl;
wofs<<L"Another test仍是测试"<<1234<<endl;
三、printf和wprintf
加上这两位C语言中的老兄,问题更加复杂。考虑以下语句(注意s的大小写):
printf("%s", "multibyte中文/n"); // ④
printf("%S", L"unicode中文/n"); // ⑤
wprintf(L"%S", "multibyte中文/n"); // ⑥
wprintf(L"%s", L"unicode中文/n"); // ⑦
缺省状况下,⑤、⑦两条语句不能输出中文,这两条语句中字符串的形式是unicode形式的。若是在全部输出语句以前加上以下语句将C语言的全局locale设置为本地语言(C语言中只有全局locale)就能够正常输出了:
setlocale(LC_CTYPE, ""); // ⑧
但这会致使cout和wcout不能输出中文,将C语言的全局locale恢复后cout和wcout就正常了,以下所示:
setlocale(LC_CTYPE, "C"); // ⑨
但恢复后,printf和wprintf输出Unicode文本又不正常了(输出MultiByte文本老是正常的)。总不能每写一个 printf/wprintf就设置一次而后再恢复一次吧?因此,建议不要混用iostream和printf/wprintf,实在要混用,那就让 printf/wprintf只输出MultiByte字符串,这样不须要调用setlocale(),也就不会影响到cout和wcout。
总结
总之,用iostream、printf/wprintf输出中文,有点麻烦。归纳起来要点以下:
若是要用wcout,须要在使用以前按语句①将其locale设置为本地语言;
若是要用ofstream或wofstream,要在打开文件以前按语句②将全局locale设为本地语言并保存初始的全局locale。而后在打开文件以后,按语句③将全局locale恢复为初始值;
不要混用iostream和printf/wprintf。若是要混用,只用printf/wprintf输出MultiByte字符串;
单独使用printf/wprintf时,若是要输出Unicode字符串,须要按语句⑧设置C语言的全局locale。若是只输出MultiByte字符串,则不需设置。
最后再加上转帖者(本站站长)的一点话:
一个程序,通常不会用两种字符串, 要么用多字节字符串, 要么用宽字符串. 这样,问题其实就很简单, 没做者说得那么复杂.. 就算有时候须要转换, 也有专门的函数(例如,多字节字符版本的程序,使用COM组件, COM组件须要宽字符串. 则能够利用 _bstr_t, CString)..测试