MSVC中带有UTF8字符的wchar_t*

MSVC中带有UTF8字符的wchar_t*,c,visual-c++,utf-8,wchar-t,C,Visual C++,Utf 8,Wchar T,我正在尝试使用vsnprintf使用UTF-8字符格式化wchar\u t*,然后使用printf打印缓冲区 给定以下代码: /* This code is modified version of KB sample: https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/rtref/vsnprintf.htm The usage of `setlocale` is required by my real-world

我正在尝试使用
vsnprintf
使用UTF-8字符格式化
wchar\u t*
,然后使用
printf
打印缓冲区

给定以下代码:

/*
  This code is modified version of KB sample:
  https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/rtref/vsnprintf.htm

  The usage of `setlocale` is required by my real-world scenario,
  but can be modified if that fixes the issue.
*/

#include <wchar.h>
#include <stdarg.h>
#include <stdio.h>
#include <locale.h>

#ifdef MSVC
#include <windows.h>
#endif

void vout(char *string, char *fmt, ...)
{
   setlocale(LC_CTYPE, "en_US.UTF-8");
   va_list arg_ptr;

   va_start(arg_ptr, fmt);
   vsnprintf(string, 100, fmt, arg_ptr);
   va_end(arg_ptr);
}

int main(void)
{
   setlocale(LC_ALL, "");
#ifdef MSVC
   SetConsoleOutputCP(65001); // with or without; no dice
#endif

   char string[100];

   wchar_t arr[] = { 0x0119 };
   vout(string, "%ls", arr);
   printf("This string should have 'ę' (e with ogonek / tail) after colon:  %s\n", string);
   return 0;
}
然而,在装有CL v19.10.25019(VS 2017)的Windows 10上,我在CMD中得到了奇怪的输出:

cl test.c /Fetest_vsn /utf-8
.\test_vsn
This string should have 'T' (e with ogonek / tail) after colon:  e
(冒号前的
ę
变为
T
,冒号后的
e
不带ogonek)

请注意,我使用了CL的新的
/utf-8
开关(在VS 2015中引入),这显然没有任何效果。根据他们的:

还有一个/utf-8选项,它是设置“/源字符集:utf-8”和“/执行字符集:utf-8”的同义词

(我的源文件已经有BOM/utf8'ness,执行字符集显然没有帮助)


为了使输出与gcc的输出看起来相同,对代码/编译器开关的最小更改量是多少?

基于@RemyLebeau的评论,我修改了代码,使用printf API的
w
变体,使输出与Windows上的msvc相同,与Unix上的gcc相同

此外,我现在使用(
FILE
translation模式),而不是更改代码页

/*
此代码是KB示例的修改版本:
https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/rtref/vsnprintf.htm
“setlocale”的使用是我的真实场景所必需的,
但如果这解决了问题,可以修改。
*/
#包括
#包括
#包括
#包括
#ifdef_WIN32
#包括//for _setmode
#包含//for _O_u16文本
#恩迪夫
void vout(wchar\u t*字符串,wchar\u t*fmt,…)
{
setlocale(LC_CTYPE,“en_US.UTF-8”);
VAU列表参数ptr;
va_启动(arg_ptr,fmt);
vswprintf(字符串,100,fmt,arg_ptr);
va_端(arg_ptr);
}
内部主(空)
{
setlocale(LC_ALL,“”);
#ifdef_WIN32
int oldmode=_setmode(_fileno(stdout),_O_16text);
#恩迪夫
wchar_t字符串[100];
wchar_t arr[]={0x0119,L'\0'};
vout(字符串,L“%ls”,arr);
wprintf(L“此字符串应在冒号%ls\r\n”后加上“ę”(e和ogonek/tail),string);
#ifdef_WIN32
_setmode(_fileno(stdout),oldmode);
#恩迪夫
返回0;
}

或者,我们可以使用
fwprintf
并提供
stdout
作为第一个参数。要对
fwprintf(stderr,format,args)
(或
perror(format,args)
)执行同样的操作,我们还需要
\u setmode
stderr。

在Windows上,
printf()
(以及通常的控制台)不支持UTF-8。您可以使用
WideCharToMultiByte()
(或等效工具)将UTF-16编码的
wchar\u t
数据转换为UTF-8,但这仍然不能保证控制台将正确显示它。您确实应该使用Unicode控制台API来编写Unicode数据到控制台,比如Win32 < Code > Wrand EndoSeWe()/Code >函数,或者< C++中的代码> STD::WcOUT 。关于如何将Unicode数据输出到Windows控制台,StackOverflow有很多问题。您的声誉很高,您应该知道在询问之前做一些研究。@vulcanraven:如果您使用Unicode API向控制台写入,您不需要使用
WideCharToMultiByte()
将Unicode数据转换为另一种编码。Windows上的Unicode API将
wchar\u t
数据作为输入。@vulcanraven:这种情况下,您应该将日志代码包装在一个自定义函数中,该函数以Unicode字符串作为输入,然后根据底层平台的需要将其写入控制台-在Ubto上为UTF-8,在Windows上为UTF-16,等等。如果区域设置出现错误,首先要检查
setlocale的返回值(LC_CTYPE,“en_US.UTF-8”)那是
NULL
?“如果无法执行选择,setlocale函数将返回空指针,并且程序的区域设置不会更改。”@RemyLebeau缺少对“在Windows上,printf()(…)不支持UTF-8”的支持。这是编译器问题,而不是操作系统问题。
cl test.c /Fetest_vsn /utf-8
.\test_vsn
This string should have 'T' (e with ogonek / tail) after colon:  e
/*
  This code is modified version of KB sample:
  https://www.ibm.com/support/knowledgecenter/en/ssw_ibm_i_73/rtref/vsnprintf.htm

  The usage of `setlocale` is required by my real-world scenario,
  but can be modified if that fixes the issue.
*/

#include <wchar.h>
#include <stdarg.h>
#include <stdio.h>
#include <locale.h>

#ifdef _WIN32
#include <io.h> //for _setmode
#include <fcntl.h> //for _O_U16TEXT
#endif

void vout(wchar_t *string, wchar_t *fmt, ...)
{
   setlocale(LC_CTYPE, "en_US.UTF-8");
   va_list arg_ptr;

   va_start(arg_ptr, fmt);
   vswprintf(string, 100, fmt, arg_ptr);
   va_end(arg_ptr);
}

int main(void)
{
   setlocale(LC_ALL, "");
#ifdef _WIN32
   int oldmode = _setmode(_fileno(stdout), _O_U16TEXT);
#endif

   wchar_t string[100];

   wchar_t arr[] = { 0x0119, L'\0' };
   vout(string, L"%ls", arr);
   wprintf(L"This string should have 'ę' (e with ogonek / tail) after colon:  %ls\r\n", string);

#ifdef _WIN32
   _setmode(_fileno(stdout), oldmode);
#endif
   return 0;
}