如何使用std::wstring
打印std::wcout
?
我尝试了以下内容,建议使用here,但它仅适用于打印此¡Hola!
但不适用于此日本
:
#include <iostream>
#include <clocale>
int main(int argc, char* argv[])
{
char* locale = setlocale(LC_ALL, "");
std::cout << "locale: " << locale << std::endl; // "C" for me
std::locale lollocale(locale);
setlocale(LC_ALL, locale);
std::wcout.imbue(lollocale);
std::wcout << L"¡Hola!" << std::endl; // ok
std::wcout << L"日本" << std::endl; // empty :(
return 0;
}
以下(建议使用here)根本不打印日文字符:
#include <stdio.h>
#include <string>
#include <locale>
#include <iostream>
using namespace std;
int main()
{
std::locale::global(std::locale(""));
wstring japan = L"日本";
wstring message = L"Welcome! Japan is ";
message += japan;
wprintf(message.c_str());
wcout << message << endl;
}
所有这些都在Mac OS 10.6.8上。使用g ++ 4.2.1,使用终端2.1.2。
终端可以一般地显示字符,例如,当我cat
源代码时。此外,此命令工作正常cout << "日本" << std::endl;
,但我需要打印wstring
。
我的$LANG
就是这样:
$ echo $LANG
en_US.UTF-8
答案 0 :(得分:6)
打印wstring的方法是将其转换为基于UTF-8字符串的字符串。严重的wchar_t is pointless以外的Windows或其他各种平台库之一,不幸的是在它变得清晰之前已经使用了wchar_t。
// move to clang and libc++ then
#include <codecvt>
int main(){
std::wstring_convert<std::codecvt_utf8<wchar_t>,wchar_t> convert; // converts between UTF-8 and UCS-4 (given sizeof(wchar_t)==4)
std:wstring s = L"日本";
std::cout << convert.to_bytes(s);
}
只是为了解释你所展示的代码出了什么问题;
char* locale = setlocale(LC_ALL, "");
std::cout << "locale: " << locale << std::endl; // "C" for me
此处的区域设置字符串是应用更改后的区域设置名称。既然你说你得到“C”就意味着你正在使用“C”语言环境。通常会有一个像“en_US.UTF-8”这样的名称,但无论出于何种原因,您的环境都没有正确设置。您显示$LANG
已正确设置,但可能其他区域设置环境变量之一设置不同。
在任何情况下,您都使用“C”语言环境,只需支持基本字符集。我相信OS X的行为是,任何char
都会直接转换为相同的wchar_t
值,而wchar_t
支持的范围内只有char
个值将转换回来。这实际上与使用基于ISO 8859-1的语言环境相同,因此日语字符不起作用。
如果你真的坚持让这个基于语言环境的东西工作,那么你需要得到一个合适的语言环境,一个使用UTF-8的语言环境。您可以找出您的环境有什么问题,也可以使用不可移植的显式语言环境名称。
std::wcout.imbue(std::locale("en_US.UTF-8"));
std::wcout << L"¡Hola!\n";
std::wcout << L"日本\n";
另外,如果您正在使用libstdc ++,您应该知道它在OS X上不能正确支持语言环境。您必须使用libc ++才能获得OS X的语言环境名称(例如,“en_US.UTF-8”)工作。
答案 1 :(得分:4)
根据libstdc ++的多个错误报告(例如http://gcc.gnu.org/bugzilla/show_bug.cgi?id=35353),C运行时和libstdc ++之间存在讨厌的交互,似乎没有人愿意尝试修复它,可能是因为utf-8“正常工作”对于大多数情况。
错误报告使用ios_base::sync_with_stdio(false)
或locale::global(...)
提及两种解决方法。
答案 2 :(得分:0)
使用nowide库以最简单的方式转换为UTF-8。然后,使用常规printf。
答案 3 :(得分:0)
默认编码为:
- Windows UTF-16。
- Linux UTF-8。
- MacOS UTF-8。
我的解决方案步骤,包括空字符\ 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;
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;
}
如果需要逆运算,则附加。
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 Literals。原始字符串后缀。
Linux代码。使用std :: cout直接打印std :: string,Linux上的默认编码为UTF-8,不需要其他功能。
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字符。
#ifdef WINDOWSLIB
void WriteLineUnicode(std::wstring ws)
{
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), ws.length(), NULL, NULL);
std::cout << std::endl;
}
void WriteUnicode(std::wstring ws)
{
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), ws.c_str(), ws.length(), NULL, NULL);
}
void WriteLineUnicode(std::string s)
{
std::wstring unicode = StringToWideString(s);
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), unicode.length(), NULL, NULL);
std::cout << std::endl;
}
void WriteUnicode(std::string s)
{
std::wstring unicode = StringToWideString(s);
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), unicode.c_str(), unicode.length(), NULL, NULL);
}
#endif
Windows代码。使用WriteConsole函数。
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并设置为default terminal on Windows。您需要将Visual Studio挂接到ConEmu。请记住,Visual Studio的exe文件是 devenv.exe
- 在带有VC ++的Microsoft Visual Studio 2017上测试; std = c ++ 17。 (Windows项目)
- 使用g ++在Microsoft Visual Studio 2017上测试; std = c ++ 17。 (Linux项目)
- 使用g ++在Jetbrains Clion 2018.3上进行测试; std = c ++ 17。 (Linux工具链/远程)
质量检查
问。为什么不使用
<codecvt>
标头函数和类? A。弃用Removed or deprecated features不能在VC ++上构建,但在g ++上则没有问题。我更喜欢0警告和头痛。Q。 Windows上的wstring是interchan。
A。弃用Removed or deprecated features不能在VC ++上构建,但在g ++上则没有问题。我更喜欢0警告和头痛。问。std :: wstring是跨平台的吗?
A。号std :: wstring使用wchar_t元素。在Windows上,wchar_t的大小为2个字节,每个字符以UTF-16单位存储,如果字符大于U + FFFF,则该字符以称为代理对的两个UTF-16单位(2个wchar_t元素)表示。在Linux上,wchar_t的大小为4个字节,每个字符存储在一个wchar_t元素中,不需要代理对。选中Standard data types on UNIX, Linux, and Windows。问。 std :: string是跨平台的吗?
A。是。 std :: string使用char元素。保证char类型在所有编译器中都是相同的字节大小。 char类型的大小为1个字节。选中Standard data types on UNIX, Linux, and Windows。