V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jayvien
V2EX  ›  Windows

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

  •  
  •   jayvien · 90 天前 · 699 次点击
    这是一个创建于 90 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 - 蓝奏云

    12 条回复    2021-05-25 19:07:55 +08:00
    missdeer
        1
    missdeer   89 天前
    用 char*试试
    wtfdsy
        2
    wtfdsy   89 天前
    格式的%hs 换成%s
    lonewolfakela
        3
    lonewolfakela   89 天前
    盲猜是 windows 的_vscwprintf 系列函数有 bug,不能正常处理 utf8,不然没法解释为啥 len=-1 但是 errno 却是 0……
    不知道楼主这段代码的需求是怎样的,能不能换别的函数进行处理?
    jones2000
        4
    jones2000   89 天前
    %ls 或%s
    ysc3839
        5
    ysc3839   89 天前 via Android
    印象中 C 里面 wchar 系列函数有坑,会根据当前 locale 进行一些转换的。
    Windows 下控制台要输出 Unicode 系列编码的字符串,建议转成 UTF-16 然后用 WriteConsole 输出。
    jayvien
        6
    jayvien   89 天前
    @lonewolfakela 经过一天的尝试,同意你的猜测,UTF-8 编码的字符串只有多字节系列的函数才能正确处理。原始需求是实现一个日志库,传多参,可以正常处理宽字节( UTF-16LE )和多字节( UTF-8 )的日志参数,并且写出 UTF-8 编码的文件。目前已经改成通过多字节系列的函数来实现了,_vscprintf 、vsprintf_s 、printf_s 、fprintf_s,写出文件的时候要用二进制模式,需要 UTF-8 BOM 头的话要自己写"\xEF\xBB\xBF"。
    jayvien
        7
    jayvien   89 天前
    需要注意的是,虽然 setlocale 设置了 UTF-8 语言环境,_fsopen 仍然不支持 UTF-8 编码的文件路径,因此需要通过 _wfsopen 打开文件。
    jayvien
        8
    jayvien   89 天前
    另外,utf8everywhere 里也有提到一些这方面的技巧,http://utf8everywhere.org/zh-cn
    ysc3839
        9
    ysc3839   89 天前 via Android
    @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
        10
    nvioue   89 天前
    ....
    兄弟你是完全不熟悉 Windows 编程吧...
    我给你们简单科普一下
    windows 凡是跟字符串相关的 api 大部分都有 A 和 W 两个版本
    你用的这个 xxxprint 系列函数我个人认为不算是 windows api. 是 VC 运行库函数, 一样的会映射多字节 multibyte 和 unicode 两个版本.
    如果你的目的是混合输出 char 和 wchar 的话... 其实这非常奇怪的需求了. 我建议你统一一下
    混合输出你必须手动 MultiByteToWideChar 和 WideCharToMultiByte. 没有人会帮你转换的. 你要知道光简体中文都有 gb2312, gbk, gb18080.. 至少 3 个编码; 你如果是输出中文需求的话可不要小瞧了这两个函数..
    打日志到文件的话没必要这么麻烦, 我告诉你用上述 api 转换完了之后直接用 WriteFile 写文件完事
    jayvien
        11
    jayvien   88 天前
    @nvioue 再过几年,如果你还在做 Windows 开发的话,你可能会理解我提的这个问题。
    nvioue
        12
    nvioue   71 天前
    @jayvien 兄弟你自己没搞清楚, 还说我不理解?
    你自己的原话: "同意你的猜测,UTF-8 编码的字符串只有多字节系列的函数才能正确处理"...
    一看就知道你没正规的学过 Windows 编程; 还在用"猜"来解决问题.... 我是好心才给你科普的.
    Windows 核心编程一书 第 2 章就是告诉你 Unicode 的部分.
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   3289 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 19ms · UTC 01:29 · PVG 09:29 · LAX 18:29 · JFK 21:29
    ♥ Do have faith in what you're doing.