【问题标题】:What most correct way to set the encoding in C++?在 C++ 中设置编码的最正确方法是什么?
【发布时间】:2013-03-27 09:52:47
【问题描述】:

如何最好地在 C++ 中设置编码?

我习惯了使用 Unicode(和 wchar_twstringwcinwcout 和 L“...”)。我还将源代码保存为 UTF-8。

目前我使用 MinGW (Windows 7) 并在 Windows 控制台 (cmd.exe) 中运行我的程序,但有时我可以在 GNU\Linux 上使用 gcc 并使用 UTF-8 编码在 Linux 控制台中运行程序。

任何时候我都想在 Windows 和 Linux 上编译我的源代码,并且我希望所有 Unicode 符号都能正确输入和输出。

当我遇到下一个编码问题时,我用 Google 搜索。我还发现了最不同的委员会:setlocale(LC_ALL, "")setlocale(LC_ALL, "xx_XX.UTF-8")std::setlocale(LC_ALL, "")std::setlocale(LC_ALL, "xx_XX.UTF-8") 来自 <clocale>

SetConsoleCP()SetConsoleOutputCP() 来自 <windows.h> 以及很多很多其他人。

终于被这个萨满教困扰了,想问你:如何建立编码是正确的?

【问题讨论】:

  • 您到底想改变什么?您想更改线程语言环境吗?系统语言环境?用户界面语言?还是活动代码页?对于线程、控制台还是系统?有很多选项,远远超过单个 setlocale 函数所暗示的。您必须先解释您希望看到的效果,然后我们才能告诉您要翻转什么开关。
  • @CodyGray,我需要正确输入和输出任何 Unicode 符号/字符串。这对效果的充分描述?我想,这意味着我需要更改启动程序的控制台的编码。
  • 一般来说,我会说程序不应该修改语言环境——它应该在提供的语言环境中工作。否则,有点违背“国际化”的目的。

标签: c++ windows unicode encoding utf


【解决方案1】:

我只需要将 Unicode 文本 输出到控制台,只有 WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), ...); 这个函数有帮助。对于输入,我假设 ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE), ...); 可以解决问题。

PSWriteOutput 的输出字符串大小有限制。因此,如果它更长,您可能希望以块的形式对其进行迭代。

【讨论】:

    【解决方案2】:

    我需要正确输入和输出任何 Unicode 符号/字符串。

    这当然是可能的,尽管使 Windows 命令提示符控制台正确识别 Unicode 需要一些特殊的魔法。不幸的是,我严重怀疑标准库函数的任何实现都会这样做。

    您会在 Stack Overflow 上找到很多关于它的问题,但 this one is a good one。基本上,控制台默认使用所谓的(有些错误的)“OEM”代码页。您想将其更改为 UTF-8 代码页,其值由 CP_UTF8 定义。为此,您需要调用SetConsoleCP 函数(设置输入 代码页)和SetConsoleOutputCP 函数(设置输出 代码页)。代码看起来像这样:

    if (!SetConsoleCP(CP_UTF8))
    {
        // An error occurred; handle it. Call GetLastError() for more information.
        // ...
    }
    if (!SetConsoleOutputCP(CP_UTF8))
    {
        // An error occurred; handle it. Call GetLastError() for more information.
        // ...
    }
    

    为了获得额外的稳健性,您可能还需要确保首先支持 UTF-8 代码页,然后再尝试设置和使用它。你可以通过调用IsValidCodePage 函数来做到这一点。例如:

    if (IsValidCodePage(CP_UTF8))
    {
        // We're all good, so set the console code page...
    }
    

    您还必须将字体从默认字体(“光栅字体”)更改为包含必要的 Unicode 字符字形的字体,例如 Lucida Console 或 Consolas (reference)。使用SetCurrentConsoleFontEx 函数很容易做到这一点。

    很遗憾,此功能在 Vista 之前的 Windows 版本中不存在。如果您绝对需要支持这些较旧的操作系统,我唯一知道要做的就是调用未记录的SetConsoleFont 函数。通常,我会强烈建议不要使用未记录的函数,但我认为这不是问题,因为您会在旧版本的操作系统中使用它。你知道那些不会改变。在可用的较新版本上,您调用支持的函数。未经测试的示例代码:

    bool IsWinVistaOrLater()
    {
        OSVERSIONINFOEX osvi;
        osvi.dwOSVersionInfoSize = sizeof(osvi);
        GetVersionEx(reinterpret_cast<LPOSVERSIONINFO>(&osvi));
    
        if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
        {
            return osvi.dwMajorVersion >= 6;
        }
        return false;
    }
    
    void SetConsoleToUnicodeFont()
    {
        HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
        if (IsWinVistaOrLater())
        {
            // Call the documented function.
            typedef BOOL (WINAPI * pfSetCurrentConsoleFontEx)(HANDLE, BOOL, PCONSOLE_FONT_INFOEX);
            HMODULE hMod = GetModuleHandle(TEXT("kernel32.dll"));
            pfSetCurrentConsoleFontEx pfSCCFX = (pfSetCurrentConsoleFontEx)GetProcAddress(hMod, "SetCurrentConsoleFontEx");
    
            CONSOLE_FONT_INFOEX cfix;
            cfix.cbSize       = sizeof(cfix);
            cfix.nFont        = 12;
            cfix.dwFontSize.X = 8;
            cfix.dwFontSize.Y = 14;
            cfix.FontFamily   = FF_DONTCARE;
            cfix.FontWeight   = 400;  // normal weight
            lstrcpy(cfix.FaceName, TEXT("Lucida Console"));
    
            pfSCCFX(hConsole,
                    FALSE, /* set font for current window size */
                    &cfix);
        }
        else
        {
            // There is no supported function on these older versions,
            // so we have to call the undocumented one.
            typedef BOOL (WINAPI * pfSetConsoleFont)(HANDLE, DWORD);
            HMODULE hMod = GetModuleHandle(TEXT("kernel32.dll"));
            pfSetConsoleFont pfSCF = (pfSetConsoleFont)GetProcAddress(hMod, "SetConsoleFont");
            pfSCF(hConsole, 12);
        }
    }
    

    请注意,我已将所需的错误检查作为练习留给读者。这里的重点是技术和可读性;将其与错误处理混为一谈只会使事情变得混乱。

    我不知道如何在 Linux 上执行这些操作。我怀疑它的工作量要少得多,因为人们告诉我操作系统在内部使用 UTF-8。无论哪种方式,你都是靠自己的;让 Windows 发出咕噜声就足以解决一个问题!

    【讨论】:

      猜你喜欢
      • 2016-12-23
      • 1970-01-01
      • 2022-11-08
      • 1970-01-01
      • 2021-08-04
      • 2017-12-17
      • 1970-01-01
      • 2010-11-24
      • 1970-01-01
      相关资源
      最近更新 更多