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

C++ copy data from std::string to std::wstring

本文关键字:std 复制 wstring string C++ 数据      更新时间:2023-10-16

假设我有一个std::string,但数据是用UTF-16编码的。
如何将该数据复制到std::wstring中,根本不修改数据?

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

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;
}

试试这个:

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);
}
开头有一个

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

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

然后对于每对字节,例如

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

其中wstd::wstringi是宽字符<s.length()/2的索引,UnsignedCharunsigned chartypedefs是保存数据的std::string,8是每字节的位数,即你必须假设或静态断言<limits.h>头的CHAR_BITS是8。>

因此,

您已将一系列表示 UTF-16 编码字符串的字节粘贴到std::string中。大概您正在执行类似反序列化表示 UTF-16 的字节的操作,并且用于检索要反序列化的字节的 API 指定了 std::string。我不认为这是最好的设计,但您将处理将其转换为 wstring,就像处理将字节转换为浮点数或其他任何东西一样;验证字节缓冲区,然后将其强制转换:

char c[] = "abxd8x3dxdcx7f";
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 只是遵循该约定。
  • 此强制转换假定数据与计算机的字节序匹配。否则,您必须交换字符串中的每个 UTF-16 代码单元。在 UTF-16 编码方案下,如果初始字节不是 0xFF0xFE 或 0xFE0xFF,并且在没有更高级别的协议的情况下,则 UTF-16 编码使用大端编码。
  • std::begin(), std::end() 和 string::d ata() 是 C++11

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


这是一个不依赖于平台特定细节的实现,只需要wchar_t足够大以容纳 UTF-16 代码单元,并且每个字符正好包含 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[] = "xFFxFEx61bx3dxd8x7fxdc";
    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";
}