2012-04-20 43 views
1

std::stringがありますが、データはUTF-16でエンコードされています。
どのようにデータをstd::wstringにコピーできますか?データをまったく変更しないでください。C++はstd :: stringからstd :: wstringへデータをコピーします

また、テキストファイルをオンラインで取得し、Content-Typeヘッダーフィールドをチェックしてエンコードを判断しているため、std::wstringを使用することはできません。しかし、std::stringを使用してデータを受信します。

+0

あり、より良い方法かもしれませんが、あなたはCスタイルの文字列を通してそれを行うと、C++に戻って変換することができます:http://www.cplusplus.com/reference/clibrary/cstdlib/mbstowcs/ – chris

答えて

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

ループの代わりに' memcpy() 'を使うのはなぜですか? 'memcpy(&result [0]、&input [0]、result.size()* 2)' –

+0

@RemyLebeau: 'memcpy'はバイト順を調整できないためです。 OPは生データのバイトオーダーが何であるかを述べていないので、 'memcpy'は時期尚早の最適化であり、正確性に悪影響を与える可能性があります。しかし、このコードには他にもいくつかの(より小さな)問題があります。Visual C++で警告を出し、関数名が誤解を招き、 'std :: runtime_error'(適切なものは' assert'でしょう)から派生した例外をスローし、不必要に負の値を扱うための2の補数、 '&'演算子。 –

1

最初にBOM(Byte Order Mark)があり、それをチェックしてバイトオーダーを決定します。それ以外の場合は、バイトオーダを知っている場合、つまり、最下位バイトまたは最上位バイトが最初に来る場合に最適です。バイトオーダーがわからず、BOMがない場合は、1つまたは両方を試し、統計テストを適用したり、ヒューマンデシジョンメーカー(HDM)を参加させるだけです。

このリトルエンディアンのバイトオーダー、つまり最下位バイトが最初にあるとします。

次に、各バイトのペアに対して、 wstd::wstringある

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

iワイド文字< s.length()/2の指標である、UnsignedCharunsigned chartypedefで、sはデータを保持std::stringで、8バイトあたりのビット数である、あなた、すなわち仮定または静的<limits.h>ヘッダからCHAR_BITSであることを主張しなければならない8

+1

の下で上位プロトコルがバイトオーダーを指定せず、シーケンスがまたはで始まらず、シーケンスがビッグエンディアンである場合、UTF-16エンコーディングスキーム。 Unicode 6.0規格の第3章段落D98を参照してください。 – bames53

+0

@ bames53:は、私が議論したBOMです。理論に関しては、それはなぜそれがうまくいくのか知っているときですが、うまくいきません。あなたが尊敬しない限り、 Windowsの規約は「上位プロトコル」(Windowsではデフォルトではリトルエンディアンです)。ウィキペディアは、「ネイティブバイトオーダリングがリトルエンディアンのコンピュータにローカルなファイルは、暗黙のうちにUTF-16LEとしてエンコードされると主張されるかもしれないので、ビッグエンディアンの推定は広く無視される」と述べている。 –

+0

Windows UTF-16エンコーディングでバイトにシリアル化すると、ほとんどの場合、またはで始まります。リトルエンディアンがなしで使用されていることは、一般的によく知られていることではありません。 UCS-2はバイトにシリアライズされているため、常にビッグエンディアンであると指定されています(もちろん、ZWNBSでテキストを開始することは完全に合法ですが)。はUCS-2シーケンスを開始できませんすべて)。 UCS-2は頻繁に間違って実行されましたが、WindowsはUTF-16を現在使用しているのでほとんど問題になりません。 – bames53

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); 
} 
+0

-1 UCS-2でエンコードされたテキストのバイトを含む 'std :: string'に対しては、実際にはしたくないです。また、機能しても、Microsoftの機能は移植性がありません。 –

+0

@ Cheersandhth.-Alfもう一度質問に答えます。第2にUCS-2が言及されていませんでした。第3に質問が窓でタグ付けされていても、類似していないとしても機能はあります。しかし、公平であるために、移植性のためにこれを書いていませんでした。 – FailedDev

+0

私は少なくとも、それはOPのいくつかのGoogleのネットワークAPIを使用しているようだね、と思う。なぜなら、Googleのものがすべてのデータを非常に不便な 'std :: string'として返すと不平を言い返しているように思われるからです... –

0

したがって、UTF-16でエンコードされた文字列を表す一連のバイトをstd::stringにスタックしました。おそらく、あなたはUTF-16を表すバイトをデシリアライズしたり、デシリアライズされるバイトを取得するためのAPIはstd :: stringを指定したりしているようです。私はそれが最高のデザインだとは思っていませんが、バイトを浮動小数点に変換する場合と同じように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 

もの:

  • このキャストは、ほとんどのプラットフォームは32ビットのwchar_tを使用するのに対し、wchar_t型は、16ビットであることを前提としています。
  • APIがwchar_t *のプラットフォームで指定されたエンコーディングであるか、またはAPIがその規約に従うだけなので、あなたのAPIは、wchar_t文字列をUTF-16として扱うことができるようにする必要があります。
  • このキャストは、データがマシンのエンディアンと一致していることを前提としています。それ以外の場合は、wstringの各UTF-16コードユニットをスワップする必要があります。 UTF-16エンコーディング方式では、最初のバイトが0xFF0xFEまたは0xFE0xFFでなく、上位プロトコルが存在しない場合、UTF-16エンコーディングはビッグエンディアンエンコーディングを使用します。
  • のstd ::()を開始、のstd :: end()は、文字列::データ()C++ 11

* UTF-16が実際にC++言語の要件を満たしていないされていますwchar_tエンコーディングでは、いくつかのプラットフォームがそれを使用しています。これは、コードポイントを処理するはずのいくつかの標準APIに問題を引き起こしますが、UTF-16コードユニットを表すwchar_tはすべてのプラットフォームのコードポイントを表すことができないからです。


ここでは、プラットフォーム固有の詳細に依存しない実装だとそれがUTF-16コード単位を保持し、各文字はUTF-16ののちょうど8ビットを保持するのに十分な大きさwchar_tのより多くの何も必要ありませんコード単位。 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"; 
} 
+0

'wchar_t'のUTF-16とC++の要件に関するコメントです。これはおそらくUTF-8と' char'の場合にも当てはまります。それは...ですか? –

+0

@ Cheersandhth.-Alfいいえ、マルチバイトエンコーディングはcharに対して明示的に許可されており、APIはそれを可能にするように設計されています。 mbstate_tの使用。 wchar_tは、サポートされている最大の文字セットのすべてのメンバーに対して異なる値を持つことになっています。サポートされている文字セットにはBMP以外の文字が含まれていない場合、および__STDC_ISO_10646__が定義されていないと仮定します(実際には、サポートされているすべての文字セットのすべての文字に別個の値を持たなければなりません)。 UTF-16は技術的に適合している可能性があります。 – bames53

関連する問題