C/C++多字节字符与宽字符的输出

本文探讨了使用C++标准库iostream及printf处理中文时遇到的问题,并提供了详细的解决方案。包括如何配置wcout输出中文、解决ofstream和wofstream的中文文件名及内容问题,以及如何混合使用iostream与printf。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用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)..
### C++多字节字符宽字节字符的区别及用法 #### 1. 基本定义 多字节字符是指字符的存储大小不固定,可能占用多个字节来表示单个字符。这种编码方式通常用于支持多种语言环境下的字符集,例如 UTF-8 或 GBK 编码[^1]。 相比之下,宽字符是一种固定的字节宽度表示方法,其典型实现为 `wchar_t` 类型,在大多数平台上占 2 或 4 字节。无论具体字符为何,宽字符始终占据相同的内存空间。 --- #### 2. 数据类型差异 在 C++ 中,多字节字符一般通过普通的 `char` 数组表示,而宽字符则使用专门的数据类型 `wchar_t` 表示: ```cpp // 多字节字符字符串 const char* mbString = "你好"; // 宽字符字符串 const wchar_t* wcString = L"你好"; ``` 上述代码展示了两种不同类型的字符串声明方式。注意宽字符字符串前需加上前缀 `L` 来区分[^2]。 --- #### 3. 转换机制 由于多字节字符宽字符之间存在本质上的差别,因此两者之间的转换不可避免涉及底层字符集的支持。以下是常见的转换函数及其用途: - **Windows 平台** - `_mbstowcs_s`: 将多字节字符串转换为宽字符字符串。 - `_wcstombs_s`: 将宽字符字符串转换回多字节字符串。 - **Linux/POSIX 平台** - `mbstowcs`: 实现类似的多字节宽字符转换功能。 - `wcstombs`: 提供反向操作的功能。 这些函数的核心在于它们依赖于当前系统的区域设置(locale)。不同的 locale 可能对应完全不同的字符映射关系。 --- #### 4. 使用场景分析 ##### (1)多字节字符适用场合 当应用程序运行在一个特定的语言环境中,并且该环境下使用的字符集较为简单时,可以优先考虑采用多字节字符处理方案。比如中文 Windows 系统默认使用 GBK 编码,此时直接利用多字节字符即可满足需求[^3]。 另外需要注意的是,UTF-8 是一种变长编码形式,虽然理论上属于广义上的多字节范畴,但由于其兼容 ASCII 的特性以及跨平台优势,在现代开发中被广泛接受并应用。 ##### (2)宽字符适合情况 对于需要统一管理各种复杂文字系统的情况来说,选用宽字符更为合适。因为每种字符都分配相同的空间量级,从而简化了许多内部逻辑运算过程。特别是在国际化软件设计领域里,推荐尽可能多地采纳宽字符技术路线。 然而值得注意的一点是,随着 Unicode 技术的发展,尤其是 UTF-16 和 UTF-32 这些基于固定单位长度的标准逐渐成为主流之后,“传统意义上的”宽字符概念正在慢慢淡出历史舞台——毕竟后者本质上也是某种特殊形态下的 Unicode 表达而已! --- #### 5. 编码方式对比 | 特性 | 多字节字符 | 宽字符 | |--------------------|-------------------------------------|-------------------------------------| | 存储单元 | 不同字符可占用不同数量的字节 | 所有字符均占有相等数量的字节 | | 主流编码实例 | UTF-8, GBK | UTF-16 (部分), UCS-2 | | 性能影响 | 访问效率较低 | 更高的访问速度 | | 内存消耗 | 较低 | 高 | 以上表格总结了两者的若干重要属性比较结果. --- ### 示例代码展示 下面给出一段简单的例子演示如何完成基本的相互转化工作: ```cpp #include <iostream> #include <cwchar> // 包含宽字符头文件 using namespace std; int main(){ const char *mbs="测试"; size_t len=mbstowcs(NULL,mbs,0)+1; // 获取所需缓冲区大小 wchatr_t *wcs=new wchar_t[len]; if(mbstowcs(wcs,mbs,len)!=(size_t)-1){ cout << "成功转换:" << wcs << endl; } delete[] wcs; } ``` 此段程序片段实现了从常规字符串至宽字符版本间的转变流程说明。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值