通过codecvt解码多字节非Unicode字符失败

问题描述 投票:0回答:1

我在 MSVC 上尝试过

std::codecvt
并遇到了多字节字符编码的问题 - 它无法从有效的多字节序列转换回来,即使在编码宽字符时可以生成这些序列:

std::locale loc(".950");
using cvttype = std::codecvt<wchar_t, char, std::mbstate_t>;
const auto &cvt = std::use_facet<cvttype>(loc);
std::wstring_convert<cvttype> conv(&cvt);
auto bytes = conv.to_bytes(L"\u4F53"); // 体
auto str = conv.from_bytes(bytes); // range_error("bad conversion")

是的,我知道从区域设置中获取一个方面会导致此处的所有权问题,但即使在通过代理方面或手动调用

std::codecvt::in
时也会出现此问题。

我已经使用其他几个代码页(例如 949 或 52936)对此进行了测试,所有结果都相同。我以前用过

std::codecvt_utf8
,没有这样的问题;只有基于语言环境的
std::codecvt
在这里失败了。

为什么会有这种行为?可以修复吗?

使用 VS 2017 编译。

c++ visual-c++ character-encoding cjk codecvt
1个回答
0
投票

原来我的程序文件夹中有msvcp140d.dll,它比系统的旧4年。无论它是简单的错误还是与系统不兼容,删除它都可以解决问题。

如果有人偶然发现同样的问题,而这不是原因,您可以先检查 C 转换是否有效:

std::setlocale(LC_ALL, ".950");
std::mbstate_t state{};
wchar_t res;
std::size_t len = std::mbrtowc(&res, &bytes[0], bytes.size(), &state);

如果

res
获得有效字符,您可以跳过
std::codecvt
机制并仅在循环中使用
std::mbrtowc

如果您无法调用

setlocale
或想直接依赖
std::locale
实例,您仍然可以,尽管需要一些技巧:

// Use your compiler version and check every time you update!
#if _MSC_VER == 1916
using cvtvec_ptr_t = std::_Locinfo::_Cvtvec(std::codecvt<wchar_t, char, std::mbstate_t>::*);

// Output variable to receive the _Cvt member
static cvtvec_ptr_t cvtvec_ptr;

namespace
{
    template <cvtvec_ptr_t Cvtvec>
    struct get_private
    {
        get_private() noexcept
        {
            cvtvec_ptr = Cvtvec;
        }
        static get_private instance;
    };

    template <cvtvec_ptr_t Cvtvec>
    get_private<Cvtvec> get_private<Cvtvec>::instance;

    // Define an object of the type, passing the private member
    template struct get_private<&std::codecvt<wchar_t, char, std::mbstate_t>::_Cvt>;
}
#endif
std::mbstate_t state{};
wchar_t res;
std::size_t len = _Mbrtowc(&res, &bytes[0], bytes.size(), &state, &(cvt.*cvtvec_ptr));

这里的技巧是使用显式模板实例化来引入一个全局变量,其初始值设定项将

cvtvec_ptr
设置为指向私有
std::codecvt<wchar_t, char, std::mbstate_t>::_Cvt
成员。这在技术上是有效的,但它使用了保留的标识符,这些标识符意味着在
std::codecvt::do_in
实现中使用,而不是在用户代码中使用。虽然这段代码可能会工作或无法编译,但显然不能保证。

© www.soinside.com 2019 - 2024. All rights reserved.