为什么在将窄字符串转换为宽字符串时用0xFF屏蔽字符?

时间:2018-04-05 09:38:52

标签: c++ c++11 wstring

将此功能考虑到convert narrow strings to wide strings

std::wstring convert(const std::string& input)
{
    try
    {
        std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
        return converter.from_bytes(input);
    }
    catch(std::range_error& e)
    {
        std::size_t length = input.length();
        std::wstring result;
        result.reserve(length);
        for(std::size_t i = 0; i < length; i++)
        {
            result.push_back(input[i] & 0xFF);
        }
        return result;
    }
}

我很难理解在后备路径中对此表达式的需求:

result.push_back(input[i] & 0xFF);

为什么字符串中的每个字符都被0xFF(0b11111111)屏蔽?

2 个答案:

答案 0 :(得分:4)

使用0xFF进行屏蔽可将任何负值减少到0-255范围内。

例如,如果您的平台的char是一个代表ISO-8859-1字符的8位有符号类型,而您的wchar_t代表UCS-2,UTF-16或UCS-4。

如果没有这种更正(或类似的内容,例如转换为unsigned charstd::byte),您会发现当提升为更宽的类型时,字符会进行符号扩展。

示例:0xa9(Unicode和拉丁语-1中的©,签名的8位中的-87)将变为\uffa9而不是\u00a9

我认为将char转换为unsigned char更加清晰 - 适用于任何大小的char,并更好地传达意图。您可以直接更改该表达式,也可以创建一个codecvt子类,为您正在执行的操作命名。

以下是如何编写和使用最小codecvt(仅适用于窄→宽转换):

#include <codecvt>
#include <locale>
#include <string>

class codecvt_latin1 : public std::codecvt<wchar_t,char,std::mbstate_t>
{
protected:
    virtual result do_in(std::mbstate_t&,
                         const char* from,
                         const char* from_end,
                         const char*& from_next,
                         wchar_t* to,
                         wchar_t* to_end,
                         wchar_t*& to_next) const override
    {
        while (from != from_end && to != to_end)
            *to++ = (unsigned char)*from++;
        from_next = from;
        to_next = to;
        return result::ok;
    }
};

std::wstring convert(const std::string& input)
{
    using codecvt_utf8 = std::codecvt_utf8<wchar_t>;
    try {
        return std::wstring_convert<codecvt_utf8>().from_bytes(input);
    } catch (std::range_error&) {
        return std::wstring_convert<codecvt_latin1>{}.from_bytes(input);
    }
}
#include <iostream>

int main()
{
    std::locale::global(std::locale{""});

    // UTF-8:  £© おはよう
    std::wcout << convert(u8"\xc2\xa3\xc2\xa9 おはよう") << std::endl;
    // Latin-1: 壩
    std::wcout << convert("\xc2\xa3\xa9") << std::endl;
}

输出:

£© おはよう
壩

答案 1 :(得分:1)

看起来转换失败时,代码只需将wstring复制到& 0FF char中就可以尝试自己的转换。

input[i]意味着&#34;清洁&#34;任何高于255的值都适合(扩展)ASCII表。这是一个无操作但是因为char会返回sizeof(char) == 1CHAR_BIT == 8,这意味着无论如何255都是最大值(在char == unsigned char和{{1}的情况下})。

等效的只是使用构造函数立即复制它们:

std::wstring result(input.begin(), input.end());
相关问题