如何最好在C ++中设置编码?
我习惯使用Unicode(和wchar_t
,wstring
,wcin
,wcout
和L“......”)。我还以UTF-8保存了源代码。
目前我使用MinGW(Windows 7)并在Windows控制台(cmd.exe)中运行我的程序,但有时我可以在GNU \ Linux上使用gcc并在Linux控制台中使用UTF-8编码运行promgram。
我总是希望在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>
。
最后我被这种萨满教所困扰,我想问你:如何建立编码是正确的?
答案 0 :(得分:6)
我需要正确地输入和输出任何Unicode符号/字符串。
这当然是可行的,尽管使Windows命令提示控制台正确地识别Unicode会带来一些特殊的魔力。遗憾的是,我严重怀疑标准库函数的任何实现都会这样做。
你会在Stack Overflow上找到一些关于它的问题,但this one is a good one。基本上,控制台默认使用所谓的(稍微错误地)“OEM”代码页。您希望将其更改为UTF-8代码页,其值由CP_UTF8
定义。为此,您需要同时调用SetConsoleCP
函数(设置输入代码页)和SetConsoleOutputCP
函数(设置输出< / em>代码页)。代码看起来像这样:
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控制台或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呜呜声对于一个答案来说已经足够了!
答案 1 :(得分:0)
我只需要将 Unicode文本输出到控制台,只有这个函数WriteConsoleW (GetStdHandle(STD_OUTPUT_HANDLE), ...);
有帮助。对于输入,我认为ReadConsoleW (GetStdHandle(STD_INPUT_HANDLE), ...);
可以解决问题。
PS :WriteOutput
输出字符串大小有限制。因此,如果时间更长,您可能希望以块的形式进行迭代。