在 Windows 上使用_vscwprintf 处理 UTF-8 编码的字符串时失败,该如何解决?

2021-05-07 07:59:47 +08:00
 jayvien
static std::wstring format_string(CONST WCHAR* pszText, ...)
{
    std::wstring result;
    va_list args;
    va_start(args, pszText);
    int len = _vscwprintf(pszText, args);
    if (len < 0)
    {
        wprintf_s(L"_vscwprintf failed, len=%i, error=%i\n", len, errno);
        return L"";
    }
    result.resize((size_t)len);
    vswprintf_s(const_cast<WCHAR*>(result.data()), result.size() + sizeof(WCHAR),
                pszText, args);
    va_end(args);
    return result;
}

int wmain(int argc, wchar_t* argv[])
{
    wprintf_s(L"%S\n", setlocale(LC_ALL, ".UTF8"));
    std::wstring msg = format_string(L"msg: %hs", u8"abc 你好");
    wprintf_s(L"msg: %s\n", msg.c_str());
    return 0;
}

输出:
Chinese (Simplified)_China.utf8
_vsctprintf failed, len=-1, error=0
msg:

操作系统:Windows 10 x64

编译器:vs2019, mingw-w64-v8.1.0

项目字符集:UNICODE

项目文件:test_utf8.zip - 蓝奏云

1354 次点击
所在节点    Windows
12 条回复
missdeer
2021-05-07 09:30:16 +08:00
用 char*试试
wtfdsy
2021-05-07 09:56:22 +08:00
格式的%hs 换成%s
lonewolfakela
2021-05-07 10:21:24 +08:00
盲猜是 windows 的_vscwprintf 系列函数有 bug,不能正常处理 utf8,不然没法解释为啥 len=-1 但是 errno 却是 0……
不知道楼主这段代码的需求是怎样的,能不能换别的函数进行处理?
jones2000
2021-05-07 11:12:27 +08:00
%ls 或%s
ysc3839
2021-05-07 13:02:26 +08:00
印象中 C 里面 wchar 系列函数有坑,会根据当前 locale 进行一些转换的。
Windows 下控制台要输出 Unicode 系列编码的字符串,建议转成 UTF-16 然后用 WriteConsole 输出。
jayvien
2021-05-07 16:03:13 +08:00
@lonewolfakela 经过一天的尝试,同意你的猜测,UTF-8 编码的字符串只有多字节系列的函数才能正确处理。原始需求是实现一个日志库,传多参,可以正常处理宽字节( UTF-16LE )和多字节( UTF-8 )的日志参数,并且写出 UTF-8 编码的文件。目前已经改成通过多字节系列的函数来实现了,_vscprintf 、vsprintf_s 、printf_s 、fprintf_s,写出文件的时候要用二进制模式,需要 UTF-8 BOM 头的话要自己写"\xEF\xBB\xBF"。
jayvien
2021-05-07 16:05:50 +08:00
需要注意的是,虽然 setlocale 设置了 UTF-8 语言环境,_fsopen 仍然不支持 UTF-8 编码的文件路径,因此需要通过 _wfsopen 打开文件。
jayvien
2021-05-07 16:58:12 +08:00
另外,utf8everywhere 里也有提到一些这方面的技巧,http://utf8everywhere.org/zh-cn
ysc3839
2021-05-07 20:31:47 +08:00
@jayvien 你要在 Windows 下使用 UTF-8 文件路径的话,只能把当前进程的 active code page 设置成 UTF-8 来实现,这个操作印象中从 Win10 某个版本开始才支持。否则你必须转换成 UTF-16 。
Windows 中的 Unicode 使用的是 UTF-16 编码。但是为了兼容性,有一些系统 API 有非 Unicode 的版本,这些非 Unicode 的版本内部一般是使用 active code page 转换成 UTF-16 。
nvioue
2021-05-08 00:16:21 +08:00
....
兄弟你是完全不熟悉 Windows 编程吧...
我给你们简单科普一下
windows 凡是跟字符串相关的 api 大部分都有 A 和 W 两个版本
你用的这个 xxxprint 系列函数我个人认为不算是 windows api. 是 VC 运行库函数, 一样的会映射多字节 multibyte 和 unicode 两个版本.
如果你的目的是混合输出 char 和 wchar 的话... 其实这非常奇怪的需求了. 我建议你统一一下
混合输出你必须手动 MultiByteToWideChar 和 WideCharToMultiByte. 没有人会帮你转换的. 你要知道光简体中文都有 gb2312, gbk, gb18080.. 至少 3 个编码; 你如果是输出中文需求的话可不要小瞧了这两个函数..
打日志到文件的话没必要这么麻烦, 我告诉你用上述 api 转换完了之后直接用 WriteFile 写文件完事
jayvien
2021-05-08 10:25:50 +08:00
@nvioue 再过几年,如果你还在做 Windows 开发的话,你可能会理解我提的这个问题。
nvioue
2021-05-25 19:07:55 +08:00
@jayvien 兄弟你自己没搞清楚, 还说我不理解?
你自己的原话: "同意你的猜测,UTF-8 编码的字符串只有多字节系列的函数才能正确处理"...
一看就知道你没正规的学过 Windows 编程; 还在用"猜"来解决问题.... 我是好心才给你科普的.
Windows 核心编程一书 第 2 章就是告诉你 Unicode 的部分.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/775324

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX