C++ 在 MSVC 编译器环境下如何向 Windows 控制台或文件正确输出中文

我没想到这系列还能出第三期,我佛了,Windows,你拿什么来赔我!

这次的问题是:在遍历窗口句柄想要输出每个窗口的标题时,中文标题无法正常输出。

废话不多说,直接上示例程序。

#include <iostream>
#include <Windows.h>

using namespace std;

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    const int n = 255;
    wchar_t className[n];
    wchar_t title[n];
    GetClassName(hwnd, className, n);
    GetWindowText(hwnd, title, n);

    wprintf(L"window title: %ls\n", title);
    wprintf(L"window class name: %ls\n\n", className);

    return TRUE;
}


int main(int argc, char* argv[])
{
    EnumWindows(EnumWindowsProc, 0);
    return 0;
}

可以看来,title 有很多 「???」字符,还有一些根本就完全不显示了。这明显是有问题的。

在上一篇文里说过,「???」出现的原因,是遇到了该代码页中不存在的值,用了一个默认的字符代替,在我这里是用 ‘?’ 代替。也就是说,在控制台也输出文件不做任何操作的默认情况下,MSVC 会采用 ANSI 代码页作为当前的字符集,在这里就是 GBK。

那么,有没有办法让 MSVC 用支持 Unicode 的编码格式进行输出呢?例如使用 utf-8 或者 utf-16 格式进行输出?

还真有。

解决方案

C 风格的文件

c++ – Output unicode strings in Windows console app – Stack Overflow

上面这个回答说了,如果是 C 风格的文件(包括 stdin 和 stdout 文件描述符),可以用 _setmode 函数改变为 utf-8 或 utf-16 输出。

_setmode(_fileno(stdout), _O_U16TEXT); // 文件将会以 utf-16 格式输出
_setmode(_fileno(stdout), _O_U8TEXT); // 文件将会以 utf-8 格式输出
#include <iostream>
#include <Windows.h>
#include <io.h> // _setmode
#include <fcntl.h> // _O_U8TEXT

using namespace std;

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    const int n = 255;
    wchar_t className[n];
    wchar_t title[n];
    GetClassName(hwnd, className, n);
    GetWindowText(hwnd, title, n);

    wprintf(L"window title: %ls\n", title);
    wprintf(L"window class name: %ls\n\n", className);

    return TRUE;
}


int main(int argc, char* argv[])
{
    _setmode(_fileno(stdout), _O_U8TEXT);
    EnumWindows(EnumWindowsProc, 0);
    return 0;
}

所以上面的代码只需要改成这样就可以输出了。

控制台可以正常显示(utf-8)

对于文件的话,也是一样的:

#include <iostream>
#include <Windows.h>
#include <io.h>
#include <fcntl.h>

using namespace std;

FILE* fp = fopen("output.txt", "w");

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    const int n = 255;
    wchar_t className[n];
    wchar_t title[n];
    GetClassName(hwnd, className, n);
    GetWindowText(hwnd, title, n);

    fwprintf(fp, L"window title: %ls\n", title);
    fwprintf(fp, L"window class name: %ls\n\n", className);

    return TRUE;
}


int main(int argc, char* argv[])
{
    fp = fopen("output.txt", "w");
    _setmode(_fileno(fp), _O_U8TEXT);
    EnumWindows(EnumWindowsProc, 0);
    return 0;
}
文件也能正常显示(utf-8)

C++ 风格的文件

c++ – Wrote to a file using std::wofstream. The file remained empty – Stack Overflow

可以用这个回答里面的方法。MSVC 提供了 codecvt_utf8  和 codecvt_utf16 ,可以用于修改文件流的编码形式。

不过我试了之后发现只有 codecvt_utf8 有效,不知道为啥 codecvt_utf16 出来的是乱码,所以示例只用 codecvt_utf8 了。

代码示例:

#include <iostream>
#include <fstream>
#include <locale>
#include <codecvt>
#include <Windows.h>

using namespace std;

wofstream out;

BOOL CALLBACK EnumWindowsProc(HWND hwnd, LPARAM lParam)
{
    const int n = 255;
    wchar_t className[n];
    wchar_t title[n];
    GetClassName(hwnd, className, n);
    GetWindowText(hwnd, title, n);

    out << L"window title: " << title << endl;
    out << L"window class name: " << className << endl << endl;

    return TRUE;
}


int main(int argc, char* argv[])
{
    out.open(L"output.txt");
    std::locale loc(std::locale::classic(), new std::codecvt_utf8<wchar_t>);
    out.imbue(loc);
    EnumWindows(EnumWindowsProc, 0);
    return 0;
}
文件中的中文能正常显示

但不知道为什么这个方法对 wcout 没用,从控制台里输出的还是乱码,所以控制台还是老老实实用上面的 _setmode 吧,这样就可以直接用 wcout 输出中文了。

作者: 梁小顺

脑子不太好用的普通人。 顺带一提性格也有点古怪。 在老妈子和厌世肥宅中来回切换。

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据