C++输出中⽂字符(转)
1. cout
场景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种⽅法中的任意⼀种都可以达到⽬的.
printf怎么输出字符
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语⾔也是这样.
Windows设计者的出发点: 统⼀使⽤ Unicode 宽字符,解决⼀切问题
原⽂:
使⽤C++标准库的iostream,可以⽅便地将控制台、⽂件、字符串以及其它可扩充的外部表⽰作为流来处理,但要处理中⽂,却会碰到很多问题。本⼈原来没怎么⽤过这个iostream,这⼏天尝试⽤这个写点东西,⼀会⼉不能输出中⽂,⼀会⼉不⽀持中⽂⽂件名的,搞得头⼤。⽹上搜了搜,没有发现适⽤于所有情况的解决⽅案。不过后来⾃⼰经过多次测试,基本解决了这些问题,现在写成⽂字作为⼀个总结,也供碰到同样问题的朋友参考。关于C语⾔中的 printf和wprintf的中⽂输出,本⽂也进⾏了探讨。
需要说明的是,我的开发环境是VS 2005(标准库当然也是微软实现的),不保证其它环境下是相同的效果。
1、cout和wcout
在缺省的C locale下,cout可以直接输出中⽂,但对于wcout却不⾏。对于wcout,需要将其locale设为本地语⾔才能输出中⽂:
wcout.imbue(locale(locale(),"",LC_CTYPE)); // ①
也有⼈⽤如下语句的,但这会改变wcout的所有locale设置,⽐如数字“1234”会输出为“1,234”。
wcout.imbue(locale(""));
2、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;
3、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)..

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。