があります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;
}
最初にBOM(Byte Order Mark)がある場合は、それをチェックしてバイト順序を決定します。それ以外の場合は、バイト順序がわかっている場合、つまり、最下位または最上位のバイトが最初に来る場合に最適です。バイト順序がわからず、BOMがない場合は、一方または両方を試して、統計的検定を適用するか、Human Decision Maker(HDM)を関与させる必要があります。
このリトルエンディアンのバイトオーダー、つまり最下位バイトが最初であるとしましょう。
次に、バイトの各ペアについて、たとえば
w.push_back( (UnsignedChar( s[2*i + 1] ) << 8u) | UnsignedChar( s[2*i] ) );
ここw
でstd::wstring
、i
はワイド文字のインデックス< s.length()/2
、UnsignedChar
はの、typedef
はデータを保持し、8はバイトあたりのビット数です。つまり、ヘッダーから8であると想定または静的にアサートする必要があります。unsigned char
s
std::string
CHAR_BITS
<limits.h>
これを試してください:
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);
}
つまり、UTF-16 でエンコードされた文字列を表す一連のバイトをstd::string
. おそらく、UTF-16 を表すバイトを逆シリアル化するようなことを行っており、逆シリアル化するバイトを取得するための API は std::string を指定しています。それが最良の設計だとは思いませんが、バイトを float などに変換する場合と同じように、wstring に変換することになります。バイト バッファを検証してからキャストします。
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 は、wchar_t エンコーディングに関する C++ 言語の要件を実際には満たしていませんが、一部のプラットフォームでは関係なく使用されています。これにより、コードポイントを処理するはずの一部の標準 API で問題が発生しますが、単に UTF-16 コード単位を表す wchar_t がプラットフォームのすべてのコードポイントを表すことができないため、処理できません。
これは、プラットフォーム固有の詳細に依存せず、wchar_t が UTF-16 コード単位を保持するのに十分な大きさであること、および各 char が UTF-16 コード単位の正確に 8 ビットを保持することだけを必要とする実装です。ただし、実際には 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";
}