假设我有std::string
,但数据以UTF-16编码
如何将该数据复制到std::wstring
,而不是根据数据修改数据?
此外,我不能只使用std::wstring
,因为我正在线检索文本文件并检查Content-Type
标头字段以确定编码。但是使用std::string
来接收数据。
答案 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] ) );
其中w
是std::wstring
,i
是宽字符的索引&lt; s.length()/2
,UnsignedChar
是typedef
的{{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
要记住的事情:
* 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";
}