C++标准为C++标准IO库设计了十分完善的国际化文本处理机制。但在实际使用中,却发现各种编译器对它的支持性存在较大的差异,很多时候无法正确的输出字符。于是我对此进行了深入的调查。

测试程序

#include <stdio.h>
#include <clocale>
#include <wchar.h>

#include <string>
#include <iostream>

using namespace std;

const char* psa = "A汉字ABC";
const wchar_t* psw = L"W汉字ABC";

int main(int argc, char* argv[])
{
    // init.
    //ios::sync_with_stdio(false);    // Linux gcc.
    locale::global(locale(""));
    //setlocale(LC_CTYPE, "");    // MinGW gcc.
    wcout.imbue(locale(""));

    // C++
    cout << psa;    cout.clear();    cout<<endl;
    wcout << psw;    wcout.clear();    wcout<<endl;

    // C
    printf("\nC:\n");
    printf("\t%s\n", psa);
    printf("\t%ls\n", psw);

    return 0;
}

理论结果

先根据C++标准,分析一下这段程序的理论结果。
在main函数中,首先执行了这两行代码对地区环境进行了初始化——

locale::global(locale(""));
wcout.imbue(locale(""));
  • locale(""):调用构造函数创建一个local,其中的空字符串具有特殊含义:使用客户环境中缺省的locale(《C++标准程序库—自修教程与参考手册》P697)。例如在简体中文系统上,会返回简体中文的locale。
  • locale::global(locale("")):将“C++标准IO库的全局locale”设为“客户环境中缺省的locale”。注意它还会设置C标准库的locale环境,造成与“setlocale(LC_ALL, "")”类似的效果(《C++标准程序库—自修教程与参考手册》P698)。
  • wcout.imbue(locale("")):使wcout使用“客户环境中缺省的locale”。

就这样,使C标准库、C++标准IO库(尤其是wcout)均正确的设置了地区环境,与客户环境中缺省环境完全匹配。
随后,使用C++标准IO库的cout、wcout分别输出窄字符串和宽字符串。

实际输出

VS 2015 / Windows 10

win10-vs2015-cout-wcout.png
经过多次测试发现,VS2015中,如果对全局的locale进行了设置,则cout无法正常输出。我们应该采用下面这种办法设置

locale loc("");
wcout.imbue(loc);

通过这种办法,不会影响全局的locale设置,cout和wcout将都能正常输出。 对于这种问题,我真的是日了微软了。