C++ 尽管存在Windows或MinGW错误,但仍能体面地显示标准输出的大UTF-8编码字符串
第二次更新:我发现了一个非常简单的问题,其实不是那么难的问题,只是问了一天后。但人们似乎心胸狭窄,因此已经有三张接近票数的选票:C++ 尽管存在Windows或MinGW错误,但仍能体面地显示标准输出的大UTF-8编码字符串,c++,utf-8,output,c++17,mingw-w64,C++,Utf 8,Output,C++17,Mingw W64,第二次更新:我发现了一个非常简单的问题,其实不是那么难的问题,只是问了一天后。但人们似乎心胸狭窄,因此已经有三张接近票数的选票: “如何在Windows命令行中使用unicode字符?”(1x)的副本: 显然不是,这已在评论中得到澄清。这与我不使用的Windows命令行工具无关 不清楚你在问什么(1x): 那你一定要忍受痛苦。例如,当我问“是否有一种简单的方法可以确定std::string中的字符是否是UTF-8符号的非结束部分?”时,我再具体不过了(实际上,标记为粗体以提高可见性),并声明这足
app.exe>out.txt 2>&1
生成的文件没有这些格式问题。所以问题是通常
std::cout执行此拆分,但底层控件
接收字符序列)必须处理正确的重新组装?
(不幸的是,在Windows上似乎没有任何东西可以处理它,除了文件
溪流。所以我仍然需要绕过这一点。最好没有
首先写入文件并显示其内容——当然
工作。)
在我使用的系统(Windows 7;MinGW-w64(GCC 8.1 for Windows))上,有一个std::cout
的错误,因此UTF-8编码的字符串在重新组装之前就被打印出来,即使它们是通过传递一个大字符串在内部被std::cout
反汇编的。下面的代码解释了bug的行为。但是,请注意,故障显示似乎是随机的,即std::cout
切片(相等)std::string
对象的方式对于程序的每次执行都是不等效的。但问题总是出现在1024倍的指数上,这就是我得出的结论
#include <iostream>
#include <sstream>
void myFaultyOutput();
void simulatedFaultyBehavior();
int main()
{
myFaultyOutput();
//simulatedFaultyBehavior();
}
void myFaultyOutput() {
std::stringstream ss; // Note that ss is built correctly (which could be shown by saving ss.str() to a file).
ss << "...";
for (int i = 0; i < 20; i++) {
for (int j = 0; j < 341; j++)
ss << u8"\u301A";
ss << "\n..";
}
std::cout << ss.str() << std::endl; // Problem occurs here, with cout.
// Note that converting ss.str() to UTF-16 std::wstring and using std::wcout results in std::wcout not
// displaying anything, not even ASCII characters in the future (until restarting the application).
}
// To display the problem on well-behaved systems ; just imagine the output would not contain newlines, while the faulty formatted characters remain.
void simulatedFaultyBehavior() {
std::stringstream ss;
int amount = 2000;
for (int j = 0; j < amount; j++)
ss << u8"\u301A";
std::string s = ss.str();
std::cout << "s.length(): " << s.length() << std::endl; // amount * 3
while (s.length() > 1024) {
std::cout << s.substr(0, 1024) << std::endl;
s = s.substr(1024);
}
std::cout << s << std::endl;
}
#包括
#包括
void myFaultyOutput();
void simulatedFaultyBehavior();
int main()
{
myFaultyOutput();
//simulatedFaultyBehavior();
}
void myFaultyOutput(){
std::stringstream ss;//请注意,ss是正确构建的(可以通过将ss.str()保存到文件中来显示)。
ss我通过实验详细阐述了一个相当简单的解决方法,我很惊讶没有人知道(我在网上没有发现类似的方法)
N.m.的尝试性回答给出了一个很好的提示,提到了特定于平台的功能\u setmode
。它“按设计”做了什么(根据和)设置文件转换模式,这是根据流程处理输入和输出流的方式。但同时,它使使用std::ostream
/std::istream
无效,但规定对格式正确的输入和输出流使用std::wostream
/std::wistream
例如,使用setmode(\u fileno(stdout),\u O\u文本)
导致了std::wcout
现在可以很好地将std::wstring
输出为UTF-8,但是std::cout
打印出垃圾字符,即使是在ASCII参数上。但是我希望能够主要使用std::string
,尤其是std::cout
进行输出。正如我所提到的,这是一种罕见的情况std::cout
的格式化失败,因此只有在打印出可能导致此问题的字符串(索引至少为1024的潜在多字符编码字符)的情况下,我才希望使用特殊的输出函数,例如couttf8string(string s)
默认值(未翻译)\u setmode
的模式是\u O_BINARY
。我们可以临时切换模式。那么为什么不切换到\u O_u8; text
,将UTF-8编码的std::string
对象转换为std::wstring
,在其上使用std::wcout
,然后切换回\u O_BINARY
?为了保持平台独立,可以不在Windows上时,只需定义通常的std::cout
调用。以下是代码:
#if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__)
#include <fcntl.h> // Also includes the non-standard file <io.h>
// (POSIX compatibility layer) to use _setmode on Windows NT.
#ifndef _O_U8TEXT // Some GCC distributions such as TDM-GCC 9.2.0 require this explicit
// definition since, depending on __MSVCRT_VERSION__, they might
// not define it.
#define _O_U8TEXT 0x40000
#endif
#endif
void coutUtf8String(string s) {
#if defined(_WIN32) || defined(WIN32) || defined(__CYGWIN__)
if (s.length() > 1024) {
// Set translation mode of wcout to UTF-8, renders cout unusable "by design"
// (see https://developercommunity.visualstudio.com/solutions/411680/view.html).
if (_setmode(STDOUT_FILENO, _O_U8TEXT) != -1) {
wcout << utf8toWide(s) << flush; // We must flush before resetting the mode.
// Set translation mode of wcout to untranslated, renders cout usable again.
_setmode(STDOUT_FILENO, _O_BINARY);
} else
// Let's use wcout anyway. Since no sink (such as Eclipse's console
// window) is attached when _setmode fails, and such sinks seem to be
// the cause for wcout to fail in default mode. The UI console view
// is filled properly like this, regardless of translation modes.
wcout << utf8toWide(s) << flush;
} else
cout << s << flush;
#else
cout << s << flush;
#endif
}
wstring utf8toWide(const char* in) {
wstring out;
if (in == nullptr)
return out;
uint32_t codepoint = 0;
while (*in != 0) {
unsigned char ch = static_cast<unsigned char>(*in);
if (ch <= 0x7f)
codepoint = ch;
else if (ch <= 0xbf)
codepoint = (codepoint << 6) | (ch & 0x3f);
else if (ch <= 0xdf)
codepoint = ch & 0x1f;
else if (ch <= 0xef)
codepoint = ch & 0x0f;
else
codepoint = ch & 0x07;
++in;
if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff)) {
if (codepoint > 0xffff) {
out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10)));
out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff)));
} else if (codepoint < 0xd800 || codepoint >= 0xe000)
out.append(1, static_cast<wchar_t>(codepoint));
}
}
return out;
}
#如果已定义(_WIN32)|已定义(WIN32)|已定义(uu CYGWIN_uu)
#include//还包括非标准文件
//(POSIX兼容层)在Windows NT上使用_setmode。
#ifndef_O_U8TEXT//一些GCC发行版,如TDM-GCC 9.2.0,需要明确的
//d