提问人:Andrew 提问时间:3/22/2010 最后编辑:vitautAndrew 更新时间:6/9/2023 访问量:100306
在 Windows 控制台中输出 Unicode 字符串
Output Unicode strings in Windows console
问:
嗨,我试图将 unicode 字符串输出到带有 iostreams 的控制台,但失败了。
我发现这个:在 c++ 控制台应用程序中使用 unicode 字体,这个代码片段有效。
SetConsoleOutputCP(CP_UTF8);
wchar_t s[] = L"èéøÞǽлљΣæča";
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
char* m = new char[bufferSize];
WideCharToMultiByte(CP_UTF8, 0, s, -1, m, bufferSize, NULL, NULL);
wprintf(L"%S", m);
但是,我没有找到任何使用 iostreams 正确输出 unicode 的方法。有什么建议吗?
这不起作用:
SetConsoleOutputCP(CP_UTF8);
utf8_locale = locale(old_locale,new boost::program_options::detail::utf8_codecvt_facet());
wcout.imbue(utf8_locale);
wcout << L"¡Hola!" << endl;
编辑除了将此片段包装在流中之外,我找不到任何其他解决方案。 希望有人有更好的想法。
//Unicode output for a Windows console
ostream &operator-(ostream &stream, const wchar_t *s)
{
int bufSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
char *buf = new char[bufSize];
WideCharToMultiByte(CP_UTF8, 0, s, -1, buf, bufSize, NULL, NULL);
wprintf(L"%S", buf);
delete[] buf;
return stream;
}
ostream &operator-(ostream &stream, const wstring &s)
{
stream - s.c_str();
return stream;
}
答:
我不认为有一个简单的答案。查看控制台代码页和 SetConsoleCP 函数,似乎需要为要输出的字符集设置适当的代码页。
wcout 必须将区域设置设置为与 CRT 不同的区域设置。以下是修复方法:
int _tmain(int argc, _TCHAR* argv[])
{
char* locale = setlocale(LC_ALL, "English"); // Get the CRT's current locale.
std::locale lollocale(locale);
setlocale(LC_ALL, locale); // Restore the CRT.
std::wcout.imbue(lollocale); // Now set the std::wcout to have the locale that we got from the CRT.
std::wcout << L"¡Hola!";
std::cin.get();
return 0;
}
我刚刚测试了它,它在这里显示字符串绝对没问题。
评论
_tmain
_TCHAR
最近,我想将 unicode 从 Python 流式传输到 Windows 控制台,这是我需要做的最低限度:
- 应将控制台字体设置为覆盖 unicode 符号的字体。没有广泛的选择: Zoo 属性 > Principle > Lucida Console
- 您应该更改当前的控制台代码页:在控制台中运行或使用 C++ 代码中的相应方法
chcp 65001
- 使用 WriteConsoleW 写入控制台
在 Windows 控制台上浏览有关 java unicode 的相关文章
此外,在 Python 中,在这种情况下,您不能写入默认的 sys.stdout,您需要使用 os.write(1, binarystring) 或直接调用 WriteConsoleW 周围的包装器来替换它。似乎在 C++ 中,您需要执行相同的操作。
评论
首先,对不起,我可能没有所需的字体,所以我还不能测试它。
这里看起来有点腥
// the following is said to be working
SetConsoleOutputCP(CP_UTF8); // output is in UTF8
wchar_t s[] = L"èéøÞǽлљΣæča";
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
char* m = new char[bufferSize];
WideCharToMultiByte(CP_UTF8, 0, s, -1, m, bufferSize, NULL, NULL);
wprintf(L"%S", m); // <-- upper case %S in wprintf() is used for MultiByte/utf-8
// lower case %s in wprintf() is used for WideChar
printf("%s", m); // <-- does this work as well? try it to verify my assumption
而
// the following is said to have problem
SetConsoleOutputCP(CP_UTF8);
utf8_locale = locale(old_locale,
new boost::program_options::detail::utf8_codecvt_facet());
wcout.imbue(utf8_locale);
wcout << L"¡Hola!" << endl; // <-- you are passing wide char.
// have you tried passing the multibyte equivalent by converting to utf8 first?
int bufferSize = WideCharToMultiByte(CP_UTF8, 0, s, -1, NULL, 0, NULL, NULL);
char* m = new char[bufferSize];
WideCharToMultiByte(CP_UTF8, 0, s, -1, m, bufferSize, NULL, NULL);
cout << m << endl;
怎么样
// without setting locale to UTF8, you pass WideChars
wcout << L"¡Hola!" << endl;
// set locale to UTF8 and use cout
SetConsoleOutputCP(CP_UTF8);
cout << utf8_encoded_by_converting_using_WideCharToMultiByte << endl;
评论
我已经使用 Visual Studio 2010 在这里验证了一个解决方案。通过这篇 MSDN 文章和 MSDN 博客文章。诀窍是对 ._setmode(..., _O_U16TEXT)
溶液:
#include <iostream>
#include <io.h>
#include <fcntl.h>
int wmain(int argc, wchar_t* argv[])
{
_setmode(_fileno(stdout), _O_U16TEXT);
std::wcout << L"Testing unicode -- English -- Ελληνικά -- Español." << std::endl;
}
截图:
评论
Doesn't work when you also have std::cout's
从 cplusplus.com:程序不应将 wcout 上的输出操作与 cout 上的输出操作(或 stdout 上的其他窄向输出操作)混合:一旦在任一上执行了输出操作,标准输出流就会获得一个方向(窄或宽),该方向只能通过在 stdout 上调用 freopen 来安全地更改。
L"안녕하세요."
L"你好!"
SetConsoleCP() 和 chcp 不一样!
以此程序片段为例:
SetConsoleCP(65001) // 65001 = UTF-8
static const char s[]="tränenüberströmt™\n";
DWORD slen=lstrlen(s);
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE),s,slen,&slen,NULL);
源代码必须保存为不带 BOM(字节顺序标记;签名)。然后,Microsoft 编译器 cl.exe 按原样采用 UTF-8 字符串。
如果此代码与 BOM 一起保存,则 cl.exe 将字符串转码为 ANSI(即 CP1252),这与 CP65001 (= UTF-8) 不匹配。
将显示字体更改为 Lucidia 控制台,否则 UTF-8 输出将根本不起作用。
- 类型:
chcp
- 答:
850
- 类型:
test.exe
- 答:
tr├ñnen├╝berstr├ÂmtÔäó
- 类型:
chcp
- 答: - 此设置已通过
SetConsoleCP()
更改,但没有有用的效果。65001
- 类型:
chcp 65001
- 类型:
test.exe
- 答: - 现在一切正常。
tränenüberströmt™
与以下设备测试: German Windows XP SP3
评论
\x45
"tr\xc3\xa4nen\xc3\xbcberstr\xc3\xb6mt\xe2\x84\xa2"
SetConsoleCP
只影响输入,所以它在你的示例中不起作用也就不足为奇了。它控制输出编码。SetConsoleOutputCP
在 Windows 控制台中正确显示西欧字符
长话短说:
- 用于查找适合您的代码页。就我而言,它是针对西欧的。
chcp
chcp 28591
- (可选)将其设为默认值:
REG ADD HKCU\Console /v CodePage /t REG_DWORD /d 28591
发现的历史
我在 Java 上遇到了类似的问题。它只是装饰性的,因为它涉及发送到控制台的日志行;但它仍然很烦人。
我们的 Java 应用程序的输出应该是 UTF-8 格式,并且在 eclipse 的控制台中正确显示。但在 Windows 控制台中,它只显示 ASCII 框绘字符:和,而不是 和 。Inicializaci├│n
artículos
Inicialización
artículos
我偶然发现了一个相关的问题,并混合了一些答案,以找到对我有用的解决方案。解决方案是更改控制台使用的代码页,并使用支持 UNICODE 的字体(如 或 )。您可以在 Windows cosole 的系统菜单中选择的字体:consolas
lucida console
- 通过以下任一方式启动控制台
Win + R
然后键入并按键。cmd
Return
- 按键并键入,然后按键。
Win
cmd
return
- 通过以下任意一项打开系统菜单
- 单击左上角图标
- 点击组合键
Alt + Space
- 然后选择“默认”以更改所有后续控制台窗口的行为
- 点击“字体”选项卡
- 选择或
Consolas
Lucida console
- 点击
OK
关于代码页,对于一次性情况,您可以使用命令完成它,然后您必须调查哪个代码页对于您的字符集是正确的。几个答案建议使用 UTF-8 代码页,即 65001,但该代码页不适用于我的西班牙语字符。chcp
另一个答案建议使用批处理脚本,以交互方式从列表中选择所需的代码页。在那里,我找到了我需要的 ISO-8859-1 的代码页:28591。所以你可以执行
chcp 28591
在每次执行应用程序之前。您可以在代码页标识符 MSDN 页中检查哪个代码页适合您。
另一个答案指示了如何将所选代码页保留为 Windows 控制台的默认代码页。它涉及更改注册表,因此请考虑自己警告,您可能会使用此解决方案使计算机变砖。
REG ADD HKCU\Console /v CodePage /t REG_DWORD /d 28591
这将使用 HKCU\Console 注册表项中的数据创建值。这确实对我有用。CodePage
28591
请注意,HKCU(“HKEY_CURRENT_USER”)仅适用于当前用户。如果要为该计算机中的所有用户更改它,则需要使用该实用程序并查找/创建相应的密钥(可能您必须在内部创建一个密钥regedit
Console
Console
HKEY_USERS\.DEFAULT
)
mswcrt 和 io 流存在一些问题。
- 技巧 _setmode(_fileno(stdout), _O_U16TEXT);仅适用于 MS VC++,不适用于 MinGW-GCC。此外,有时它会导致崩溃,具体取决于 Windows 配置。
- SetConsoleCP(65001) 用于 UTF-8。在许多多字节字符方案中可能会失败,但对于 UTF-16LE 始终可以
- 您需要在应用程序退出时还原预览控制台代码页。
Windows 控制台支持 UNICODE 和 UTF-16LE 模式下的 ReadConsole 和 WriteConsole 函数。背景效果 - 在这种情况下,管道将不起作用。即 myapp.exe >> ret.log 将文件设置为 0 字节 ret.log。如果您对此感到满意,您可以尝试我的库,如下所示。
const char* umessage = "Hello!\nПривет!\nПривіт!\nΧαιρετίσματα!\nHelló!\nHallå!\n";
...
#include <console.hpp>
#include <ios>
...
std::ostream& cout = io::console::out_stream();
cout << umessage
<< 1234567890ull << '\n'
<< 123456.78e+09 << '\n'
<< 12356.789e+10L << '\n'
<< std::hex << 0xCAFEBABE
<< std::endl;
库会自动将您的 UTF-8 转换为 UTF-16LE,并使用 WriteConsole 将其写入控制台。以及错误和输入流。 图书馆的另一个好处 - 颜色。
示例应用上的链接:https://github.com/incoder1/IO/tree/master/examples/iostreams
图书馆主页:https://github.com/incoder1/IO
Unicode Hello World 中文
这里有一个中文的Hello World。实际上它只是“你好”。我在 Windows 10 上测试了它,但我认为它可能从 Windows Vista 开始工作。 在Windows Vista之前,如果您想要一个编程解决方案,而不是配置控制台/注册表等,这将是困难的。如果您真的需要在 Windows 7 上执行此操作,也许可以看看这里: 更改控制台字体 Windows 7
我不想声称这是唯一的解决方案,但这就是对我有用的方法。
大纲
- Unicode 项目设置
- 将控制台代码页设置为 unicode
- 查找并使用支持要显示的字符的字体
- 使用要显示的语言的区域设置
- 使用宽字符输出,即
std::wcout
1 项目设置
我正在使用 Visual Studio 2017 CE。我创建了一个空白的控制台应用。默认设置没问题。但是,如果您遇到问题或使用其他 IDE,您可能需要检查以下内容:
在项目属性中,找到配置属性 -> 常规 ->项目默认值 ->字符集。它应该是“使用 Unicode 字符集”而不是“多字节”。
这将为您定义和预处理器宏。_UNICODE
UNICODE
int wmain(int argc, wchar_t* argv[])
另外,我认为我们应该使用函数而不是.它们都可以工作,但在 unicode 环境中可能更方便。wmain
main
wmain
此外,我的源文件是 UTF-16-LE 编码的,这似乎是 Visual Studio 2017 中的默认设置。
2 控制台代码页
这是很明显的。我们需要控制台中的 unicode 代码页。
如果要检查默认代码页,只需打开控制台并键入任何参数即可。
我们必须将其更改为 65001,即 UTF-8 代码页。Windows 代码页标识符该代码页有一个预处理器宏:。
我需要同时设置输入和输出代码页。当我省略任何一个时,输出不正确。chcp
CP_UTF8
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
您可能还需要检查这些函数的布尔返回值。
3 选择字体
到目前为止,我还没有找到支持每个字符的控制台字体。所以我不得不选择一个。 如果要输出部分仅以一种字体提供,部分以另一种字体提供的字符,那么我相信不可能找到解决方案。只有当有一种字体支持每个字符时,才可能。但我也没有研究如何安装字体。
我认为不可能在同一控制台窗口中同时使用两种不同的字体。
如何找到兼容的字体? 打开控制台,单击窗口左上角的图标转到控制台窗口的属性。转到“字体”选项卡,选择一种字体,然后单击“确定”。然后尝试在控制台窗口中输入您的字符。重复此操作,直到找到可以使用的字体。然后记下字体的名称。
此外,您还可以在属性窗口中更改字体的大小。 如果找到满意的尺寸,请记下属性窗口中“所选字体”部分中显示的大小值。它将以像素为单位显示宽度和高度。
若要以编程方式实际设置字体,请使用:
CONSOLE_FONT_INFOEX fontInfo;
// ... configure fontInfo
SetCurrentConsoleFontEx(hConsole, false, &fontInfo);
有关详细信息,请参阅本答案末尾的示例。 或者在精美的手册中查找它:SetCurrentConsoleFont。 此函数仅从 Windows Vista 开始存在。
4 设置区域设置
您需要将区域设置设置为要打印的字符的语言的区域设置。
char* a = setlocale(LC_ALL, "chinese");
返回值很有意思。它将包含一个字符串来准确描述所选择的区域设置。
试一试吧:-)
我用 和 进行了测试。
更多信息: setlocalechinese
german
5 使用宽字符输出
这里没什么好说的。 如果要输出宽字符,请使用以下示例:
std::wcout << L"你好" << std::endl;
哦,别忘了宽字符的前缀!
如果在源文件中键入这样的文字 Unicode 字符,则源文件必须是 unicode 编码的。就像 Visual Studio 中的默认值一样,是 UTF-16-LE。或者使用记事本++并将编码设置为.L
UCS-2 LE BOM
例
最后,我把它们放在一起作为一个例子:
#include <Windows.h>
#include <iostream>
#include <io.h>
#include <fcntl.h>
#include <locale.h>
#include <wincon.h>
int wmain(int argc, wchar_t* argv[])
{
SetConsoleTitle(L"My Console Window - 你好");
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
char* a = setlocale(LC_ALL, "chinese");
SetConsoleOutputCP(CP_UTF8);
SetConsoleCP(CP_UTF8);
CONSOLE_FONT_INFOEX fontInfo;
fontInfo.cbSize = sizeof(fontInfo);
fontInfo.FontFamily = 54;
fontInfo.FontWeight = 400;
fontInfo.nFont = 0;
const wchar_t myFont[] = L"KaiTi";
fontInfo.dwFontSize = { 18, 41 };
std::copy(myFont, myFont + (sizeof(myFont) / sizeof(wchar_t)), fontInfo.FaceName);
SetCurrentConsoleFontEx(hConsole, false, &fontInfo);
std::wcout << L"Hello World!" << std::endl;
std::wcout << L"你好!" << std::endl;
return 0;
}
干杯!
于 2021-11-20 编辑
也许您也可以尝试使用新的 Windows 终端。它似乎开箱即用地打印 unicode。您仍然需要在设置中配置支持您的字符的字体。它由 Microsoft 在 github 上作为开源开发,您也可以从 Microsoft Store 安装它。我在 Windows 10 上成功尝试了这个。
评论
SetCurrentConsoleFontEx
函数处于支持模式,不再鼓励在新的命令行应用中使用。
默认编码:
- Windows UTF-16 格式。
- Linux UTF-8 格式。
- MacOS UTF-8 格式。
我的解决方案步骤,包括 null chars \0(避免截断)。在不使用 windows.h 标头上的函数的情况下:
- 添加宏以检测平台。
#if defined (_WIN32)
#define WINDOWSLIB 1
#elif defined (__ANDROID__) || defined(ANDROID)//Android
#define ANDROIDLIB 1
#elif defined (__APPLE__)//iOS, Mac OS
#define MACOSLIB 1
#elif defined (__LINUX__) || defined(__gnu_linux__) || defined(__linux__)//_Ubuntu - Fedora - Centos - RedHat
#define LINUXLIB 1
#endif
- 创建将 std::w字符串转换为 std::string 的函数,反之亦然。
#include <locale>
#include <iostream>
#include <string>
#ifdef WINDOWSLIB
#include <Windows.h>
#endif
using namespace std::literals::string_literals;
// Convert std::wstring to std::string
std::string WidestringToString(const std::wstring& wstr, const std::string& locale)
{
if (wstr.empty())
{
return std::string();
}
size_t pos;
size_t begin = 0;
std::string ret;
size_t size;
#ifdef WINDOWSLIB
_locale_t lc = _create_locale(LC_ALL, locale.c_str());
pos = wstr.find(static_cast<wchar_t>(0), begin);
while (pos != std::wstring::npos && begin < wstr.length())
{
std::wstring segment = std::wstring(&wstr[begin], pos - begin);
_wcstombs_s_l(&size, nullptr, 0, &segment[0], _TRUNCATE, lc);
std::string converted = std::string(size, 0);
_wcstombs_s_l(&size, &converted[0], size, &segment[0], _TRUNCATE, lc);
ret.append(converted);
begin = pos + 1;
pos = wstr.find(static_cast<wchar_t>(0), begin);
}
if (begin <= wstr.length()) {
std::wstring segment = std::wstring(&wstr[begin], wstr.length() - begin);
_wcstombs_s_l(&size, nullptr, 0, &segment[0], _TRUNCATE, lc);
std::string converted = std::string(size, 0);
_wcstombs_s_l(&size, &converted[0], size, &segment[0], _TRUNCATE, lc);
converted.resize(size - 1);
ret.append(converted);
}
_free_locale(lc);
#elif defined LINUXLIB
std::string currentLocale = setlocale(LC_ALL, nullptr);
setlocale(LC_ALL, locale.c_str());
pos = wstr.find(static_cast<wchar_t>(0), begin);
while (pos != std::wstring::npos && begin < wstr.length())
{
std::wstring segment = std::wstring(&wstr[begin], pos - begin);
size = wcstombs(nullptr, segment.c_str(), 0);
std::string converted = std::string(size, 0);
wcstombs(&converted[0], segment.c_str(), converted.size());
ret.append(converted);
ret.append({ 0 });
begin = pos + 1;
pos = wstr.find(static_cast<wchar_t>(0), begin);
}
if (begin <= wstr.length()) {
std::wstring segment = std::wstring(&wstr[begin], wstr.length() - begin);
size = wcstombs(nullptr, segment.c_str(), 0);
std::string converted = std::string(size, 0);
wcstombs(&converted[0], segment.c_str(), converted.size());
ret.append(converted);
}
setlocale(LC_ALL, currentLocale.c_str());
#elif defined MACOSLIB
#endif
return ret;
}
// Convert std::string to std::wstring
std::wstring StringToWideString(const std::string& str, const std::string& locale)
{
if (str.empty())
{
return std::wstring();
}
size_t pos;
size_t begin = 0;
std::wstring ret;
size_t size;
#ifdef WINDOWSLIB
_locale_t lc = _create_locale(LC_ALL, locale.c_str());
pos = str.find(static_cast<char>(0), begin);
while (pos != std::string::npos) {
std::string segment = std::string(&str[begin], pos - begin);
std::wstring converted = std::wstring(segment.size() + 1, 0);
_mbstowcs_s_l(&size, &converted[0], converted.size(), &segment[0], _TRUNCATE, lc);
converted.resize(size - 1);
ret.append(converted);
ret.append({ 0 });
begin = pos + 1;
pos = str.find(static_cast<char>(0), begin);
}
if (begin < str.length()) {
std::string segment = std::string(&str[begin], str.length() - begin);
std::wstring converted = std::wstring(segment.size() + 1, 0);
_mbstowcs_s_l(&size, &converted[0], converted.size(), &segment[0], _TRUNCATE, lc);
converted.resize(size - 1);
ret.append(converted);
}
_free_locale(lc);
#elif defined LINUXLIB
std::string currentLocale = setlocale(LC_ALL, nullptr);
setlocale(LC_ALL, locale.c_str());
pos = str.find(static_cast<char>(0), begin);
while (pos != std::string::npos) {
std::string segment = std::string(&str[begin], pos - begin);
std::wstring converted = std::wstring(segment.size(), 0);
size = mbstowcs(&converted[0], &segment[0], converted.size());
converted.resize(size);
ret.append(converted);
ret.append({ 0 });
begin = pos + 1;
pos = str.find(static_cast<char>(0), begin);
}
if (begin < str.length()) {
std::string segment = std::string(&str[begin], str.length() - begin);
std::wstring converted = std::wstring(segment.size(), 0);
size = mbstowcs(&converted[0], &segment[0], converted.size());
converted.resize(size);
ret.append(converted);
}
setlocale(LC_ALL, currentLocale.c_str());
#elif defined MACOSLIB
#endif
return ret;
}
- 打印 std::string。 检查 RawString 后缀。
Linux 代码。使用 std::cout 直接打印 std::string。
如果您有 std::wstring.
1. 转换为 std::string。
2. 使用 std::cout 打印。
std::wstring x = L"\0\001日本ABC\0DE\0F\0G🐶\0"s;
std::string result = WidestringToString(x, "en_US.UTF-8");
std::cout << "RESULT=" << result << std::endl;
std::cout << "RESULT_SIZE=" << result.size() << std::endl;
在 Windows 上,如果您需要打印 unicode。我们需要使用 WriteConsole 从 std::wstring 或 std::string 打印 unicode 字符。
void WriteUnicodeLine(const std::string& s)
{
#ifdef WINDOWSLIB
WriteUnicode(s);
std::cout << std::endl;
#elif defined LINUXLIB
std::cout << s << std::endl;
#elif defined MACOSLIB
#endif
}
void WriteUnicode(const std::string& s)
{
#ifdef WINDOWSLIB
std::wstring unicode = Insane::String::Strings::StringToWideString(s);
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), static_cast<DWORD>(unicode.length()), nullptr, nullptr);
#elif defined LINUXLIB
std::cout << s;
#elif defined MACOSLIB
#endif
}
void WriteUnicodeLineW(const std::wstring& ws)
{
#ifdef WINDOWSLIB
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), static_cast<DWORD>(ws.length()), nullptr, nullptr);
std::cout << std::endl;
#elif defined LINUXLIB
std::cout << String::Strings::WidestringToString(ws)<<std::endl;
#elif defined MACOSLIB
#endif
}
void WriteUnicodeW(const std::wstring& ws)
{
#ifdef WINDOWSLIB
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), static_cast<DWORD>(ws.length()), nullptr, nullptr);
#elif defined LINUXLIB
std::cout << String::Strings::WidestringToString(ws);
#elif defined MACOSLIB
#endif
}
Windows 代码。使用 WriteLineUnicode 或 WriteUnicode 函数。相同的代码可用于 Linux。
std::wstring x = L"\0\001日本ABC\0DE\0F\0G🐶\0"s;
std::string result = WidestringToString(x, "en_US.UTF-8");
WriteLineUnicode(u8"RESULT" + result);
WriteLineUnicode(u8"RESULT_SIZE" + std::to_string(result.size()));
最后在 Windows 上。您需要在控制台中对 unicode 字符提供强大而完整的支持。我推荐 ConEmu 并设置为 Windows 上的默认终端。
在 Microsoft Visual Studio 和 Jetbrains Clion 上进行测试。
- 在带有 VC++ 的 Microsoft Visual Studio 2017 上测试;标准=C++17。(Windows 项目)
- 在带有 g++ 的 Microsoft Visual Studio 2017 上测试;标准=C++17。(Linux 项目)
- 在 Jetbrains Clion 2018.3 上使用 g++ 进行测试;标准=C++17。(Linux 工具链/远程)
质量保证
问。为什么不使用标头函数和类?
A. 弃用 已删除或弃用的功能不可能在 VC++ 上构建,但在 g++ 上没有问题。我更喜欢 0 个警告和头痛。<codecvt>
Q. Windows 上的 wstring 是 interchan。
A. 弃用 已删除或弃用的功能不可能在 VC++ 上构建,但在 g++ 上没有问题。我更喜欢 0 个警告和头痛。Q. std ::wstring 是跨平台的吗?
答:不可以。std::wstring 使用wchar_t元素。在 Windows 上,wchar_t大小为 2 个字节,每个字符以 UTF-16 单位存储,如果字符大于 U+FFFF,则该字符以两个 UTF-16 单位(2 个wchar_t元素)表示,称为代理项对。在 Linux 上wchar_t大小为 4 个字节,每个字符存储在一个wchar_t元素中,不需要代理项对。检查 UNIX、Linux 和 Windows 上的标准数据类型。Q. std ::string 是跨平台的吗?
答:是的。std::string 使用 char 元素。保证 char 类型在所有编译器中的字节大小相同。char 类型大小为 1 字节。检查 UNIX、Linux 和 Windows 上的标准数据类型。
在 C++23 中,您将能够使用 std::p rint
便携式打印 Unicode 文本:
import std;
int main() {
std::print("Шчучыншчына");
}
输出:
Шчучыншчына
这需要使用 MSVC 中的编译器选项进行编译。/utf-8
在它可用之前,您可以使用开源的 {fmt} 库,是基于。例如:std::print
#include <fmt/core.h>
int main() {
fmt::print("Шчучыншчына");
}
我不建议使用,因为它是不可移植的,如果没有额外的努力,它甚至无法在 Windows 上运行,例如:wcout
std::wcout << L"èéøÞǽлљΣæča";
将打印:
├и├й├╕├Ю╟╜╨╗╤Щ╬г├ж─Нa
在西里尔语 Windows(ACP 1251,控制台 CP 866)中。
免责声明:我是 {fmt} 和 C++23 的作者。std::print
在具有英国区域设置的 Win10 下从 VS2017 运行控制台应用程序需要我:
- 设置 VS2017 工具 > 环境 > 字体和颜色 > 字体:例如“Lucida”
- 使用编码“Unicode (UTF-8 with signature) - Codepage 650001”保存C++源文件,以便您可以在没有编译器警告的情况下键入重音字符 L“âéïôù”,同时避免到处使用双字节字符
- 使用“使用多字节”标志>常规>字符集>配置属性以及 C/C++ > 所有选项>附加选项>>“/utf-8”标志进行编译
- #include < iostream>、<io.h> 和 <fcntl.h>
- 在应用程序启动时执行一次晦涩难懂的“_setmode(_fileno(stdout), _O_WTEXT);”
- 忘记 'cout <<... ;' 而只使用 'wcout << ... ;'
对于备忘录,Win7 上的 VS2015 需要“SetConsoleOutputCP(65001)”,并允许通过 wcout 和 cout 混合输出。
如果你正在寻找一个可移植的解决方案,不幸的是,它仍然不是C++20标准的一部分,我可以推荐nowide库。它既可以独立使用,也可以作为 boost 的一部分。您会发现许多标准对应物在那里使用或发出 utf-8 编码的 s。是的,s,不是 s(还)。如果您的程序已经运行了 s char8_t,请随意使用 s-remediation 实用程序将 s 解释为 s。char
char
char8_t
char8_t
char
请求的代码片段如下所示:
#include <boost/nowide/iostream.hpp>
#include <char8_t-remediation.h>
int main()
{
using boost::nowide::cout;
cout << U8("¡Hola!") << std::endl;
}
注意:请注意流方向问题。在我的回答中,一个简短的建议是:仅对输入/输出和 utf-8 编码数据使用无宽流。
就我而言,我正在读取 UTF-8 文件并打印到 ,我发现效果非常好,即使在 Visual Studio 调试器中也能正确显示 UTF-8 单词(我正在阅读繁体中文),来自这篇文章:Console
wifstream
#include <sstream>
#include <fstream>
#include <codecvt>
std::wstring readFile(const char* filename)
{
std::wifstream wif(filename);
wif.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));
std::wstringstream wss;
wss << wif.rdbuf();
return wss.str();
}
// usage
std::wstring wstr2;
wstr2 = readFile("C:\\yourUtf8File.txt");
wcout << wstr2;
解决方案 1:使用 WCHAR
有一件事总是有效的:在所有地方使用宽字符。喜欢
const wchar_t* str = L"你好\n";
DWORD nwritten = 0;
WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), str, 3, &nwritten, NULL);
Unicode 是非特定语言的。您可以使用任何语言,并且不会有编码问题。你想使用 UTF-8 吗?好。首先使用 MultiByteToWideChar 将其转换为宽字符字符串。
在继续阅读下面的其他解决方案之前,请注意这个解决方案有一个独特的优势:它不依赖于系统或用户的区域设置。
解决方案 2:正确设置系统区域设置和用户区域设置。它们应该是一样的。
我假设 Windows 的 UTF-8 语言环境还没有出现。然后,您需要知道您将使用哪种语言(中文,法语?),并更改系统设置以匹配它。有系统级设置:
请将它们设置为相同的语言。
然后,在程序中,将 “setlocale(LC_ALL, ”“);” 插入到 main 函数中。这是一个通用规则,无论您使用哪种操作系统,每当您想使用标准库来处理 ASCII 以外的字符集时,您都应该有这行代码。否则,区域设置默认为“C”,并且仅包含 ASCII。然后你就可以开始使用 std::wcout 和 C 函数了,比如 fputws。
在 Win10 上使用 VS2019 和 UNICODE 控制台应用进行测试时发现以下测试西班牙语和日语:
如果你只是一个字符串,那么你会得到错误的西班牙语字符(日语未测试,但肯定它不起作用)。默认的“C”语言环境默认值似乎是 ASCII(PC 的传统扩展 ASCII 排序表)。wprintf
using:使用西班牙语(墨西哥)Windows 语言设置时,将正确的代码页设置为 CP1252,输出良好(lucida 控制台字体)。但是,日语输出(使用日语 Windows 语言)被禁止显示(这意味着这些字符没有输出,输出普通拉丁字符)。setlocale(LC_ALL, "");
using: '_setmode(_fileno(stdout), _O_U16TEXT);' 输出对所有输出都正常工作。但是,所有输出都是 16 位,因此重定向到文件会输出 16 位字符。
using: 和文本输出也可以使用(但如果您在之后设置它,则不会 - 我必须删除它才能使输出正常工作)。printf
UTF-8
SetConsoleOutputCP(CP_UTF8)
setlocale(LC_ALL, "");
字体:对于亚洲字符,请使用 MS Mincho,对于其他字符,您可以使用 Lucida Console。
我在希伯来语中使用了一个回文词,因为控制台应用程序可能会显示从右到左颠倒的字符串。
这是我的多平台代码:
#include <iostream>
#ifdef _WIN32 // #A
#include <io.h> // #B
#include <fcntl.h> // #C
#else // #D
#include <locale> // #E
#endif
int main()
{
#ifdef _WIN32 // #A
_setmode(_fileno(stdout), _O_U16TEXT); // #F
std::wcout << L"אבא" << std::endl; // #G
#else // #D
std::locale::global(std::locale("")); // #H
std::wcout.imbue(std::locale()); // #I
std::wcout << L"אבא" << std::endl; // #G
#endif
}
#A - 特定于 Windows 的代码的预处理器指令
#B - 包括用于低级别 I/O 操作的 io.h 库
#C - 包括用于文件控制操作的 fcntl.h 库
#D - 非 Windows 代码的预处理器指令
#E - 包括用于特定于区域设置的操作的区域设置库
#F - 将 stdout 的模式设置为使用 Unicode
#G - 将希伯来语单词打印到控制台
#H - 将全局区域设置设置为用户的首选区域设置
#I - 将 wcout 的区域设置设置为全局区域设置
评论
WriteConsoleW
- 如果它不起作用,那么这是不可能的。