vfwprintf_s 函数与 std::cout 同时输出中文的问题

www_adintr_com 2018-04-25 04:18:00
使用的时候发现, vfwprintf_s 与 std::cout 只能有一个正常输出中文, 无论怎么设置 local 都不行.

环境 Visual C++ 2015
测试代码如下:

#include "stdafx.h"
#include <stdio.h>
#include <stdarg.h>
#include <iostream>

void printChinese(int x = 0)
{
va_list va;
va_start(va, x);

std::cout.clear();

printf("-----------\n");
vfwprintf_s(stdout, L"Print函数\n", va);
std::cout << "Cout流" << std::endl;
}

int main()
{
setlocale(LC_CTYPE, "C");
printChinese();

setlocale(LC_CTYPE, "");
printChinese();

setlocale(LC_CTYPE, ".936");
printChinese();

setlocale(LC_CTYPE, "chs");
printChinese();

std::cout.imbue(std::locale::classic());
printChinese();

std::cout.imbue(std::locale::locale("C"));
printChinese();

return 0;
}


输出结果为:

-----------
PrintCout流
-----------
Print函数
Cout-----------
Print函数
Cout-----------
Print函数
Cout-----------
Print函数
Cout-----------
Print函数
Cout


可见, 只有把 local 设置为 "C" 时 std::cout 才能输出中文, 而此时 vfwprintf_s 函数无法输出中文.
将 local 设置为中文后, vfwprintf_s 可以输出中文, 而 std::cout 无法输出中文, 单独调整 std::cout 的 locale 无效.

将 locale 设置为 "C", 让 std::cout 正常输出, 把 vfwprintf_s 换成 _vfwprintf_p_l 函数, 单独设置 locale 仍然无效, 测试代码如下:


#include "stdafx.h"
#include <stdio.h>
#include <stdarg.h>
#include <iostream>
#include <locale.h>

void printChinese(int x = 0)
{
va_list va;
va_start(va, x);

std::cout.clear();

printf("-----------\n");

_locale_t chs_local = _create_locale(LC_CTYPE, ".936");
_vfwprintf_p_l(stdout, L"Print函数\n", chs_local, va);
_free_locale(chs_local);

std::cout << "Cout流" << std::endl;
}

int main()
{
setlocale(LC_CTYPE, "C");
printChinese();
return 0;
}


输出


-----------
PrintCout流



请教各位, 除了每输出一次就 setlocale 切换一次 locale, 要如何才能在同一个程序里让 vfwprintf_s 函数与 std::cout 都能输出中文?

...全文
1368 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
paschen 版主 2018-04-26
  • 打赏
  • 举报
回复
昨天给你测试时是在WIN7环境
paschen 版主 2018-04-26
  • 打赏
  • 举报
回复
引用 12 楼 www_adintr_com的回复:
已确认是 Windows SDK 的 BUG.
在 Windows Kit 10 的 10.0.10150.0, 10.0.10240.0 版本中存在, 在 10.0.16299.0 版本中已修复.
在前两个版的 write.cpp 150 行的 if (source_it < buffer_end) 在 10.0.16299.0 版本中被修正为 if ((source_it + 1) < buffer_end) 解决了此 BUG
通过设置 VC2015 使用的 SDK 版本已解决了此问题.
之前也遇到过TransparentBlt在某种情况下显示死活不正常,后来发现是SDK BUG
赵4老师 2018-04-26
  • 打赏
  • 举报
回复
请将cout改为wcout 仅供参考:
#pragma warning(disable:4786)
#include <iostream>
#include <string>
#include <map>
using namespace std;
int main () {
    wstring ws1,ws2;

    wcin.imbue(locale("chs"));
    wcout.imbue(locale("chs"));
    getline(wcin,ws1);
    getline(wcin,ws2);
    if (ws1.size()!=ws2.size()) {
        wcout<<ws1<<L" 和 "<<ws2<<L" 不对应"<<endl;
        return 1;
    }

    map<wchar_t,wchar_t> m1,m2;
    int n=ws1.size();

    for (int i=0;i<n;i++) {
        if (m1.find(ws1[i])==m1.end() && m2.find(ws2[i])==m2.end()) {
            m1[ws1[i]]=ws2[i];
            m2[ws2[i]]=ws1[i];
        } else {
            if (m1[ws1[i]]!=ws2[i] || m2[ws2[i]]!=ws1[i]) {
                wcout<<ws1<<L" 和 "<<ws2<<L" 不对应"<<endl;
                return 1;
            }
        }
    }
    wcout<<ws1<<L" 和 "<<ws2<<L" 对应"<<endl;
    return 0;
}
www_adintr_com 2018-04-26
  • 打赏
  • 举报
回复
已确认是 Windows SDK 的 BUG.
在 Windows Kit 10 的 10.0.10150.0, 10.0.10240.0 版本中存在, 在 10.0.16299.0 版本中已修复.
在前两个版的 write.cpp 150 行的 if (source_it < buffer_end) 在 10.0.16299.0 版本中被修正为 if ((source_it + 1) < buffer_end) 解决了此 BUG
通过设置 VC2015 使用的 SDK 版本已解决了此问题.
zilaishuichina 2018-04-26
  • 打赏
  • 举报
回复
引用 10 楼 adlay 的回复:
更换编译器将会导致很多的库需要重新编译, 暂时先不考虑. 现在看到的是只有 setlocale(LC_CTYPE, "C"); 时 std::cout 才能正常输出中文, imbue 对 std::cout 似乎无效, 只对 std::wcout 有效
因为按你给的例子代码, 我这的运行结果和你的不一样, 我和1楼是一样的,所以要不你可以先测一下输出到文本文件当中,看看是什么结果,先排除cmd控制台自身的问题 然后,你的项目环境如果是UNICODE字符集,然后setlocale(LC_CTYPE, "C"); ,在这两个条件不,w某某某系列的函数应该都是有问题的,用不带w的函数应该都是能正常输出中文的,就是输出char*类型的带中文的字符串是没有问题的,std::cout也是一样可以的。 就你给的例子代码来分析一下

    setlocale(LC_CTYPE, "C");
    printChinese(); 
    // 你的输出Print,但输出不了函数这两个字,我这边测试也是这样,因为"C"不支持wchar*的中文输出
    // 你的输出Cout流,我这边测试也是这样,"C"下面输出char*的中文,是没有问题的
 
    setlocale(LC_CTYPE, "");
    printChinese();
    // 你的 输出Print函数,我这边测试也是这样,因为中文环境下,用w某某某这一系列的函数,输出wchar*的中文,是没有问题的
    // 你的输出Cout,但输出不了流这个汉字,我这边测试不是这样,我这是能输出流这个汉字,中文环境下,用cout,输出char*的中文,是没有问题的

    setlocale(LC_CTYPE, ".936");
    printChinese();
     // 同 setlocale(LC_CTYPE, "");

    setlocale(LC_CTYPE, "chs");
    printChinese();
    // 同 setlocale(LC_CTYPE, "");

    std::cout.imbue(std::locale::classic());
    printChinese();
    // 你的 输出Print函数,我这边测试也是这样
    // 但是这里有个问题,如果没有前面的代码,只是单纯的设置std::cout.imbue(std::locale::classic());,w某某某的系列函数,是输出不了wchar*的中文的
    // 只是由于你给的例子代码,在std::cout.imbue(std::locale::classic());之前, 还设置了setlocale(LC_CTYPE, "chs");
    // 猜测如果先调用过了setlocale,再调用imbue是没有效果的,或者是有冲突的,没有研究过具体的代码,只是根据我这的运行现象做的猜测
    // 所以你的代码只是凑巧,在在std::cout.imbue(std::locale::classic());之后,w某某某的系列函数还能正确的输出wchar*的中文的
    // 你的输出Cout,但输出不了流这个汉字,我这边测试不是这样,我这是能输出流这个汉字的
    // 这里和凑不凑巧就没有关系了,因为不管是设置"C",还是"chs",cout应该都是能输出char*的中文的
 
    std::cout.imbue(std::locale::locale("C"));
    printChinese();
    // 同std::cout.imbue(std::locale::classic());
www_adintr_com 2018-04-26
  • 打赏
  • 举报
回复
引用 8 楼 zilaishuichina 的回复:
[quote=引用 6 楼 adlay 的回复:] 1. 不是乱码, 是根本就不输出, 而且会将流置于一种错误状态. 2 .项目使用的是 UNICODE 字符集, 但是没办法把所有代码中的 std::cout 都替换成 std::wcout, 那将引起把 std::string 贴换成 std::wstring 的连锁反应, 所以想要 wprintf 系列函数和 std::cout 同时使用(std::wcout 是正常的). 使用 hacker 的办法也行, 只要在当前系统能工作, 不用管是不是符合规范, 是不是可移植这些. 3. 我相信你这个是规范的写法, 但是现在需要让不规范的代码跑起来有什么办法没?
我的是vs2017, 要不你用2017试试,也有可能是编译器的bug 然后setlocale(LC_CTYPE, "C"); 肯定是不行的, 得是中文字符集的local,imbue也得设置为中文字符集的local,你的std::locale::classic()和std::locale("C")是一个东西[/quote] 更换编译器将会导致很多的库需要重新编译, 暂时先不考虑. 现在看到的是只有 setlocale(LC_CTYPE, "C"); 时 std::cout 才能正常输出中文, imbue 对 std::cout 似乎无效, 只对 std::wcout 有效
www_adintr_com 2018-04-26
  • 打赏
  • 举报
回复
引用 3 楼 paschen 的回复:
[quote=引用 2 楼 www_adintr_com的回复:][quote=引用 1 楼 paschen 的回复:] 我机器上运行你代码结果:
你测试的环境是什么?[/quote] 我的是VS2013,你看下跟踪进标准库代码分析下原因[/quote] 跟踪发现, std::cout 最终会调用 fputc, 对中文, locale 为 C 时 fputc 返回输出的字符串, locale 为 chs 时 fputc 返回 EOF. 继续跟踪 fputc 发现, 对不同的 locale, write_requires_double_translation_nolock(1) 会返回不同的值, 从而走向不同的分支. 在 write_requires_double_translation_nolock 中通过获取线程的 local 是否为 C 来做了一个判断, 源代码如下: write.cpp:87~92

    // 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;
对此有何解决办法没?
zilaishuichina 2018-04-26
  • 打赏
  • 举报
回复
引用 6 楼 adlay 的回复:
1. 不是乱码, 是根本就不输出, 而且会将流置于一种错误状态. 2 .项目使用的是 UNICODE 字符集, 但是没办法把所有代码中的 std::cout 都替换成 std::wcout, 那将引起把 std::string 贴换成 std::wstring 的连锁反应, 所以想要 wprintf 系列函数和 std::cout 同时使用(std::wcout 是正常的). 使用 hacker 的办法也行, 只要在当前系统能工作, 不用管是不是符合规范, 是不是可移植这些. 3. 我相信你这个是规范的写法, 但是现在需要让不规范的代码跑起来有什么办法没?
我的是vs2017, 要不你用2017试试,也有可能是编译器的bug 然后setlocale(LC_CTYPE, "C"); 肯定是不行的, 得是中文字符集的local,imbue也得设置为中文字符集的local,你的std::locale::classic()和std::locale("C")是一个东西
jiht594 2018-04-26
  • 打赏
  • 举报
回复
既然都用UNICODE 了,就全转了吧。以后也省事。
www_adintr_com 2018-04-26
  • 打赏
  • 举报
回复
引用 5 楼 zilaishuichina 的回复:
1、 如果你用setlocale(LC_CTYPE, "C"); 再想输出wchar,是肯定乱码的,"C"的字符集是不支持wchar的 The "C" locale is the minimal locale. It is a rather neutral locale which has the same settings across all systems and compilers, and therefore the exact results of a program using this locale are predictable. This is the locale used by default on all C programs. 2、项目属性的字符集,vs的项目上右键属性,有一个字符集设置,是UNICODE还是多字节, 如果设置了UNICODE字符集,代码中一般就用w某某某的函数系列,比如wprintf, fwprintf, swprintf,_wsetlocale,这一系列的函数,如果是多字节,就用printf, fprintf, sprintf,setlocale这一系列的函数。printf系列,和wprintf系列,一般不混用。 3、稍微改了一下你的例子,仅供参考

#include <stdio.h>
#include <stdarg.h>
#include <iostream>

void printChineseCpp(int x = 0, ...)
{
	va_list va;
	va_start(va, &x);

#ifdef UNICODE
	wchar_t *p = va_arg(va, wchar_t *);
	std::wcout << L"-----------" << std::endl;
	std::wcout << L"Cout流," << p << std::endl;
#else
	char *p = va_arg(va, char *);
	std::cout << "-----------" << std::endl;
	std::cout << "Cout流," << p << std::endl;
#endif

	va_end(va);
}

void printChineseC(int x = 0, ...)
{
	va_list va;
	va_start(va, &x);

#ifdef UNICODE
	wprintf(L"-----------\n");
	vfwprintf_s(stdout, L"Print函数,%ls\n", va);
#else
	printf("-----------\n");
	vfprintf_s(stdout, "Print函数,%s\n", va);
#endif

	va_end(va);
}

int main(int argc, char* argv[])
{
#ifdef UNICODE

	std::wstring curLocale = _wsetlocale(LC_CTYPE, NULL);
	_wsetlocale(LC_CTYPE, L"C");
	printChineseC(1, L"一");
	_wsetlocale(LC_CTYPE, L"");
	printChineseC(2, L"二");
	_wsetlocale(LC_CTYPE, L".936");
	printChineseC(3, L"三");
	_wsetlocale(LC_CTYPE, L"chs");
	printChineseC(4, L"四");
	_wsetlocale(LC_CTYPE, curLocale.c_str());

	std::wcout.clear();
	std::wcout.imbue(std::locale("C"));
	printChineseCpp(5, L"五");
	std::wcout.clear();
	std::wcout.imbue(std::locale(""));
	printChineseCpp(6, L"六");
	std::wcout.clear();
	std::wcout.imbue(std::locale(".936"));
	printChineseCpp(7, L"七");
	std::wcout.clear();
	std::wcout.imbue(std::locale("chs"));
	printChineseCpp(8, L"八");

#else

	std::string curLocale = setlocale(LC_CTYPE, NULL);
	setlocale(LC_CTYPE, "C");
	printChineseC(1, "一");
	setlocale(LC_CTYPE, "");
	printChineseC(2, "二");
	setlocale(LC_CTYPE, ".936");
	printChineseC(3, "三");
	setlocale(LC_CTYPE, "chs");
	printChineseC(4, "四");
	setlocale(LC_CTYPE, curLocale.c_str());

	std::cout.clear();
	std::cout.imbue(std::locale("C"));
	printChineseCpp(5, "五");
	std::cout.clear();
	std::cout.imbue(std::locale(""));
	printChineseCpp(6, "六");
	std::cout.clear();
	std::cout.imbue(std::locale(".936"));
	printChineseCpp(7, "七");
	std::cout.clear();
	std::cout.imbue(std::locale("chs"));
	printChineseCpp(8, "八");

#endif

	return 0;
}
使用UNICODE字符集,则输出:(其中两个设置为“C”的,输出为乱码) ----------- Print----------- Print函数,二 ----------- Print函数,三 ----------- Print函数,四 ----------- Cout----------- Cout流,六 ----------- Cout流,七 ----------- Cout流,八 使用多字节字符集,则输出: ----------- Print函数,一 ----------- Print函数,二 ----------- Print函数,三 ----------- Print函数,四 ----------- Cout流,五 ----------- Cout流,六 ----------- Cout流,七 ----------- Cout流,八
1. 不是乱码, 是根本就不输出, 而且会将流置于一种错误状态. 2 .项目使用的是 UNICODE 字符集, 但是没办法把所有代码中的 std::cout 都替换成 std::wcout, 那将引起把 std::string 贴换成 std::wstring 的连锁反应, 所以想要 wprintf 系列函数和 std::cout 同时使用(std::wcout 是正常的). 使用 hacker 的办法也行, 只要在当前系统能工作, 不用管是不是符合规范, 是不是可移植这些. 3. 我相信你这个是规范的写法, 但是现在需要让不规范的代码跑起来有什么办法没?
「已注销」 2018-04-26
  • 打赏
  • 举报
回复
等下我试下再
zilaishuichina 2018-04-25
  • 打赏
  • 举报
回复
1、 如果你用setlocale(LC_CTYPE, "C"); 再想输出wchar,是肯定乱码的,"C"的字符集是不支持wchar的 The "C" locale is the minimal locale. It is a rather neutral locale which has the same settings across all systems and compilers, and therefore the exact results of a program using this locale are predictable. This is the locale used by default on all C programs. 2、项目属性的字符集,vs的项目上右键属性,有一个字符集设置,是UNICODE还是多字节, 如果设置了UNICODE字符集,代码中一般就用w某某某的函数系列,比如wprintf, fwprintf, swprintf,_wsetlocale,这一系列的函数,如果是多字节,就用printf, fprintf, sprintf,setlocale这一系列的函数。printf系列,和wprintf系列,一般不混用。 3、稍微改了一下你的例子,仅供参考

#include <stdio.h>
#include <stdarg.h>
#include <iostream>

void printChineseCpp(int x = 0, ...)
{
	va_list va;
	va_start(va, &x);

#ifdef UNICODE
	wchar_t *p = va_arg(va, wchar_t *);
	std::wcout << L"-----------" << std::endl;
	std::wcout << L"Cout流," << p << std::endl;
#else
	char *p = va_arg(va, char *);
	std::cout << "-----------" << std::endl;
	std::cout << "Cout流," << p << std::endl;
#endif

	va_end(va);
}

void printChineseC(int x = 0, ...)
{
	va_list va;
	va_start(va, &x);

#ifdef UNICODE
	wprintf(L"-----------\n");
	vfwprintf_s(stdout, L"Print函数,%ls\n", va);
#else
	printf("-----------\n");
	vfprintf_s(stdout, "Print函数,%s\n", va);
#endif

	va_end(va);
}

int main(int argc, char* argv[])
{
#ifdef UNICODE

	std::wstring curLocale = _wsetlocale(LC_CTYPE, NULL);
	_wsetlocale(LC_CTYPE, L"C");
	printChineseC(1, L"一");
	_wsetlocale(LC_CTYPE, L"");
	printChineseC(2, L"二");
	_wsetlocale(LC_CTYPE, L".936");
	printChineseC(3, L"三");
	_wsetlocale(LC_CTYPE, L"chs");
	printChineseC(4, L"四");
	_wsetlocale(LC_CTYPE, curLocale.c_str());

	std::wcout.clear();
	std::wcout.imbue(std::locale("C"));
	printChineseCpp(5, L"五");
	std::wcout.clear();
	std::wcout.imbue(std::locale(""));
	printChineseCpp(6, L"六");
	std::wcout.clear();
	std::wcout.imbue(std::locale(".936"));
	printChineseCpp(7, L"七");
	std::wcout.clear();
	std::wcout.imbue(std::locale("chs"));
	printChineseCpp(8, L"八");

#else

	std::string curLocale = setlocale(LC_CTYPE, NULL);
	setlocale(LC_CTYPE, "C");
	printChineseC(1, "一");
	setlocale(LC_CTYPE, "");
	printChineseC(2, "二");
	setlocale(LC_CTYPE, ".936");
	printChineseC(3, "三");
	setlocale(LC_CTYPE, "chs");
	printChineseC(4, "四");
	setlocale(LC_CTYPE, curLocale.c_str());

	std::cout.clear();
	std::cout.imbue(std::locale("C"));
	printChineseCpp(5, "五");
	std::cout.clear();
	std::cout.imbue(std::locale(""));
	printChineseCpp(6, "六");
	std::cout.clear();
	std::cout.imbue(std::locale(".936"));
	printChineseCpp(7, "七");
	std::cout.clear();
	std::cout.imbue(std::locale("chs"));
	printChineseCpp(8, "八");

#endif

	return 0;
}
使用UNICODE字符集,则输出:(其中两个设置为“C”的,输出为乱码) ----------- Print----------- Print函数,二 ----------- Print函数,三 ----------- Print函数,四 ----------- Cout----------- Cout流,六 ----------- Cout流,七 ----------- Cout流,八 使用多字节字符集,则输出: ----------- Print函数,一 ----------- Print函数,二 ----------- Print函数,三 ----------- Print函数,四 ----------- Cout流,五 ----------- Cout流,六 ----------- Cout流,七 ----------- Cout流,八
paschen 版主 2018-04-25
  • 打赏
  • 举报
回复
另外,VS2015对标准库实现有较大的调整,具体可参看:

https://docs.microsoft.com/en-us/cpp/porting/visual-cpp-change-history-2003-2015

以下内容摘字链接:

localeconv The localeconv function declared in locale.h now works correctly when per-thread locale is enabled. In previous versions of the library, this function would return the lconv data for the global locale, not the thread's locale.
If you use per thread locale, you should check your use of localeconv to see if your code assumes that the lconv data returned is for the global locale and modify it appropriately.


paschen 版主 2018-04-25
  • 打赏
  • 举报
回复
引用 2 楼 www_adintr_com的回复:
[quote=引用 1 楼 paschen 的回复:] 我机器上运行你代码结果:
你测试的环境是什么?[/quote] 我的是VS2013,你看下跟踪进标准库代码分析下原因
www_adintr_com 2018-04-25
  • 打赏
  • 举报
回复
引用 1 楼 paschen 的回复:
我机器上运行你代码结果:
你测试的环境是什么?
paschen 版主 2018-04-25
  • 打赏
  • 举报
回复
我机器上运行你代码结果:

64,642

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧