C ++将数据从std :: string复制到std :: wstring

时间:2012-04-20 14:06:18

标签: c++ windows string unicode encoding

假设我有std::string,但数据以UTF-16编码 如何将该数据复制到std::wstring,而不是根据数据修改数据?

此外,我不能只使用std::wstring,因为我正在线检索文本文件并检查Content-Type标头字段以确定编码。但是使用std::string来接收数据。

4 个答案:

答案 0 :(得分:2)

std::wstring PackUTF16(const std::string & input)
{
    if (input.size() % 2 != 0)
        throw std::invalid_argument("input length must be even");
    std::wstring result(input.size() / 2, 0);
    for (int i = 0;  i < result.size();  ++i)
    {
        result[i] = (input[2*i+1] & 0xff) << 8 | (input[2*i] & 0xff); // for little endian
        //result[i] = (input[2*i] & 0xff) << 8 | (input[2*i+1] & 0xff); // for big endian
    }
    return result;
}

答案 1 :(得分:1)

在开始时有一个BOM(字节顺序标记),然后检查确定字节顺序。否则,最好知道字节顺序,即最低有效或最高有效字节是否优先。如果您不知道字节顺序且没有BOM,那么您只需尝试其中一个或两个并应用一些统计测试和/或涉及人工决策者(HDM)。

假设这个Little Endian字节顺序,即最低有效字节优先。

然后为每对字节做例如。

w.push_back( (UnsignedChar( s[2*i + 1] ) << 8u) | UnsignedChar( s[2*i] ) );

其中wstd::wstringi是宽字符的索引&lt; s.length()/2UnsignedChartypedef的{​​{1}},unsigned char是保存数据的s,8是每字节的位数,即您必须假设或静态断言std::string标题中的CHAR_BITS为8。

答案 2 :(得分:1)

试试这个:

static inline std::wstring charToWide(const std::string & s_in)
{
    const char * cs = s_in.c_str();
    size_t aSize;
    if( ::mbsrtowcs_s(&aSize, NULL, 0, &cs, 0, NULL) != 0)
    {
      throw std::exception("Cannot convert string");
    }  
    std::vector<wchar_t> aBuffer(aSize);
    size_t aSizeSec;
    if (::mbstowcs_s(&aSizeSec, &aBuffer[0], aSize, cs, aSize) != 0)
    {
      throw std::exception("Cannot convert string");
    } 
    return std::wstring(&aBuffer[0], aSize - 1);
}

答案 3 :(得分:0)

因此,您将一系列表示UTF-16编码字符串的字节粘贴到std::string中。据推测,您正在执行类似于反序列化表示UTF-16的字节的操作,并且用于检索要反序列化的字节的API指定了std :: string。我不认为这是最好的设计,但你会把它转换成一个wstring,就像你处理将字节转换为float或其他任何东西一样;验证字节缓冲区,然后投射它:

char c[] = "\0a\0b\xd8\x3d\xdc\x7f";
std::string buf(std::begin(c),std::end(c));
assert(0==buf.size()%2);
std::wstring utf16(reinterpret_cast<wchar_t const *>(buf.data()),buf.size()/sizeof(wchar_t));
// also validate that each code unit is legal, and that there are no isolated surrogates

要记住的事情:

  • 此演员假定wchar_t为16位,而大多数平台使用32位wchar_t。
  • 为了有用,您的API需要能够将wchar_t字符串视为UTF-16,因为这是wchar_t *的平台指定编码,或者因为API只遵循该约定。
  • 此演员假定数据与机器的endianess匹配。否则你必须在wstring中交换每个UTF-16代码单元。在UTF-16编码方案下,如果初始字节不是0xFF0xFE或0xFE0xFF并且缺少更高级别的协议,那么UTF-16编码使用大端编码。
  • std :: begin(),std :: end()和string :: data()是C ++ 11

* UTF-16实际上并不符合C ++语言对wchar_t编码的要求,但有些平台无论如何都使用它。这导致了一些标准API的问题,这些API应该处理代码点,但不能简单地因为代表UTF-16代码单元的wchar_t不能代表所有平台的代码点。


这是一个不依赖于平台特定细节的实现,只需要wchar_t足够大以容纳UTF-16代码单元,并且每个char只包含8位UTF-16代码单元。它实际上并没有验证UTF-16数据。

#include <string>
#include <cassert>

#include <iterator>
#include <algorithm>
#include <iostream>

enum class endian {
    big,little,unknown
};

std::wstring deserialize_utf16be(std::string const &s) {
    assert(0==s.size()%2);

    std::wstring ws;
    for(size_t i=0;i<s.size();++i)
        if(i%2)
            ws.back() = ws.back() | ((unsigned char)s[i] & 0xFF);
        else
            ws.push_back(((unsigned char)s[i]  & 0xFF) << 8);
    return ws;
}

std::wstring deserialize_utf16le(std::string const &s) {
    assert(0==s.size()%2);

    std::wstring ws;
    for(size_t i=0;i<s.size();++i)
        if(i%2)
            ws.back() = ws.back() | (((unsigned char)s[i] & 0xFF) << 8);
        else
            ws.push_back((unsigned char)s[i] & 0xFF);
    return ws;
}

std::wstring deserialize_utf16(std::string s, endian e=endian::unknown) {
    static_assert(std::numeric_limits<wchar_t>::max() >= 0xFFFF,"wchar_t must be large enough to hold UTF-16 code units");
    static_assert(CHAR_BIT>=8,"char must hold 8 bits of UTF-16 code units");
    assert(0==s.size()%2);

    if(endian::big == e)
        return deserialize_utf16be(s);
    if(endian::little == e)
        return deserialize_utf16le(s);

    if(2<=s.size() && ((unsigned char)s[0])==0xFF && ((unsigned char)s[1])==0xFE)
        return deserialize_utf16le(s.substr(2));
    if(2<=s.size() && ((unsigned char)s[0])==0xfe && ((unsigned char)s[1])==0xff)
        return deserialize_utf16be(s.substr(2));

    return deserialize_utf16be(s);
}


int main() {
    char c[] = "\xFF\xFE\x61\0b\0\x3d\xd8\x7f\xdc";
    std::string buf(std::begin(c),std::end(c)-1);
    std::wstring utf16 = deserialize_utf16(buf);
    std::cout << std::hex;
    std::copy(begin(utf16),end(utf16),std::ostream_iterator<int>(std::cout," "));
    std::cout << "\n";
}