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

比方说,我有一个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)。

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

然后,每对字节做例如

 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。 我不认为这是最好的设计,但你会把它转换成一个字符串,就像你处理字节转换为浮动或其他任何东西一样; 验证字节缓冲区,然后投它:

 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遵循该约定。
  • 这个转换假定数据与机器的字节匹配。 否则,您必须在wstring中交换每个UTF-16代码单元。 在UTF-16编码方案下,如果初始字节不是0xFF0xFE或0xFE0xFF,并且缺少更高级别的协议,则UTF-16编码使用大端编码。
  • std :: begin(),std :: end()和string :: data()是C ++ 11

* UTF-16实际上并不符合C ++语言对wchar_t编码的要求,但有些平台无论如何都使用它。 这会导致一些标准的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"; }