Welcome to A!Die Software Studio |
在使用中发现, 一旦设置了 setlocale(LC_ALL, "chs"); 之后, std::cout 就不能在控制台输出中文了. 只有设置 setlocale(LC_ALL, "C"); 才能正确输出中文.
用 std::cout.imbue(std::locale::locale("C")); 单独设置 std::cout 的 locale 也无效.
跟踪发现, std::cout 最终会调用 fputc, 对中文, locale 为 C 时 fputc 返回输出的字符串, locale 为 chs 时 fputc 返回 EOF.
继续跟踪 fputc 发现, 对不同的 locale, write_requires_double_translation_nolock 会返回不同的值, 从而走向不同的分支.
在 write_requires_double_translation_nolock 中通过获取线程的 local 是否为 C 来做了一个判断, 源代码如下:
write.cpp:87~92
2
3
4
5
6
|
// Get the current locale. If we're in the C locale and the file is open // in ANSI mode, we don't need double translation: __acrt_ptd* const ptd = __acrt_getptd(); bool const is_c_locale = ptd->_locale_info->locale_name[LC_CTYPE] == nullptr; if (is_c_locale && _textmode(fh) == __crt_lowio_text_mode::ansi) return false ; |
对于 locale 不是 C 的, 输出会通过这个判断转向 write_double_translated_ansi_nolock 函数来执行, 而在这个函数执行时将会在文件第 153 行的 if (mbtowc(&wc, source_it, 2) == -1) 处返回 -1 参数错误. 原因在于 source_it 的内存中只包含了一个字符, 而传入 mbtowc 函数时却使用了 2 作为内存的大小.
通过对比不同版本的 SDK, 发现在 C:\Program Files (x86)\Windows Kits\10\Source\10.0.10150.0\ucrt\lowio\write.cpp 和 C:\Program Files (x86)\Windows Kits\10\Source\10.0.10240.0\ucrt\lowio\write.cpp 中的第 150 行 if (source_it < buffer_end) 在新版本的 SDK C:\Program Files (x86)\Windows Kits\10\Source\10.0.16299.0\ucrt\lowio\write.cpp 中已经被修改为 if ((source_it + 1) < buffer_end) 了. 这应该就是为了修正这个 BUG 的.
在 VC2015 中将项目属性的 目标平台版本设置 改为 10.0.16299.0, 编译产生错误:
LINK : fatal error LNK1158: 无法运行“rc.exe”
查看运行目录发现, VC2015 为该 SDK 添加的执行目录为 C:\Program Files (x86)\Windows Kits\10\bin\x86, 而此 SDK 存放编译程序的目录实际为 C:\Program Files (x86)\Windows Kits\10\bin\10.0.16299.0\x86, 于是手动添加该目录后重新编译通过.
使用新版本的 SDK 后该 BUG 解决, std::cout 可以正确输出中文了.