3

std::wstring私が一緒にいる方法を使用してMultiByteToWideChar

std::wstring widen(const std::string &in)
{
    int len = MultiByteToWideChar(CP_UTF8, 0, &in[0], -1, NULL, 0);
    std::wstring out(len, 0);
    MultiByteToWideChar(CP_UTF8, 0, &in[0], -1, &out[0], len);
    return out;
}
4

4 に答える 4

5

あなたが尋ねているなら、おそらくうまくいくでしょう。それが正しいか?

  1. in.c_str()代わりに使用する必要があります&in[0]
  2. MultiByteToWideChar少なくとも初回の戻り値を確認する必要があります。
  3. MultiByteToWideChar(-1) の長さで呼び出された場合、成功した場合、ゼロ ターミネータが考慮されます(つまり、成功すると常に >= 1 が返されます)。の長さコンストラクターstd::wstringはこれを必要としません。std::wstring(5,0)6 つのワイド文字にスペースを割り当てます。5+ゼロターム。したがって、技術的には、1 つ多すぎるワイド文字を割り当てています。

MultiByteToWideCharドキュメントからcbMultiByte-1:

このパラメータが -1 の場合、関数は終端の null 文字を含む入力文字列全体を処理します。したがって、結果の Unicode 文字列には終端の null 文字があり、関数によって返される長さにはこの文字が含まれます。

于 2013-01-06T17:40:20.317 に答える
5

への最初の呼び出しに問題がありますMultiByteToWideChar: 文字シーケンスがゼロで終了することは保証されていません (実際には通常そうです)。その行を次のように変更します

int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), -1, NULL, 0);

そしてあなたは安全であるべきです。がMultiByteToWideChar失敗して 0 を返したとしても、これはlenへの 2 番目の呼び出しで最終パラメータとして渡すことによって考慮されますMultiByteToWideChar

そうは言っても、クラッシュしたりメモリを破壊したりしないという意味では安全です。ただし、もう 1 つ問題があります。入力文字列が原因MultiByteToWideCharで失敗しない限り、返される文字列はsize()本来より 1 文字大きいと主張します。次のようにコードを変更することをお勧めします。

std::wstring widen(std::string const &in)
{
    std::wstring out{};

    if (in.length() > 0)
    {
        // Calculate target buffer size (not including the zero terminator).
        int len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
                                      in.c_str(), in.size(), NULL, 0);
        if ( len == 0 )
        {
            throw std::runtime_error("Invalid character sequence.");
        }

        out.resize(len);
        // No error checking. We already know, that the conversion will succeed.
        MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
                            in.c_str(), in.size(), &out[0], out.size());
                            // Use out.data() in place of &out[0] for C++17
    }

    return out;
}

この実装は、次の問題に対処します。

  • MB_ERR_INVALID_CHARSフラグを渡すことにより、入力シーケンスが有効な UTF-8 でない場合にエラーを報告します。
  • エラーは、例外をスローすることによって報告されます。これにより、変換エラーと、サイズがゼロの文字列を返す呼び出しの成功を区別できます。(注: c'tor は、失敗した場合に既に例外をスローしています。他のエラーに対して例外をスローしないstd::wstringのは不自然に感じるでしょう。 )
  • 実装は、埋め込みNUL文字を含む入力を適切に処理します。これはめったに使用されませんが、使用される場合 (たとえば、OPENFILENAMElpstrFilterメンバーを構成する場合)、その理由で (静かに) 失敗することはありません。
  • 戻り値のコンテナー ストレージが過剰に割り当てられることはありません。への呼び出しでcbMultiByte引数が に設定されている場合、返される長さにはゼロ ターミネータのスペースが含まれます。ただし、この文字は実装によって所有され、変換される文字シーケンスの一部ではありません。-1MultiByteToWideCharstd::string
  • 前の箇条書きに関連して、この実装はゼロ ターミネータを変換しません。元のコードはそうで、返された文字列は、メンバーが呼び出されNULたときに文字列の末尾に 2 文字を生成します。c_str()
于 2013-01-06T17:33:36.740 に答える
0

他の答えは良いですが、同じ問題に関する私自身の調査に基づいて、将来の訪問者のためにいくつかの追加情報を追加したいと思います.

  1. Microsoft の開発者である Larry Osterman は、リターン コード チェックと NRVO (名前付き戻り値の最適化) に関する非常に優れた点を含む、このような関数を説明する優れたブログ投稿を持っています。まだ利用可能な場合は、議論のために投稿を読む必要があります。投稿が行方不明になった場合に備えて、彼の最終的なコードを含めます。

    
    std::wstring UnicodeStringFromAnsiString(_In_ const std::string &ansiString)
    {
        std::wstring returnValue;
        auto wideCharSize = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ansiString.c_str(), -1, nullptr, 0);
        if (wideCharSize == 0)
        {
            return returnValue;
        }
        returnValue.resize(wideCharSize);
        wideCharSize = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, ansiString.c_str(), -1, &returnValue[0], wideCharSize);
        if (wideCharSize == 0)
        {
            returnValue.resize(0);
            return returnValue;
        }
        returnValue.resize(wideCharSize-1);
        return returnValue;
    }
    

私自身の使用法では、ブログのコメントに記載されている最適化を追加することができ、ANSI 文字列の長さに -1 は必要ありませんでした。

  1. C++17 (セクション 21.3.1.7.1)は、変更可能なポインターを取得する代わりに使用する必要がある、新しく追加された非 constdata()メソッドを文書化しています。&in[0]

    charT* data() noexcept;

  2. STL\0は結果の末尾を所有するc_str()ため、文字列サイズの操作方法に注意してください。

于 2016-08-24T12:19:45.013 に答える
0

いいえ、 astd::wstringはそのデータをメモリの連続したブロックに格納することが保証されていないためです (ただし、実装ではそうする可能性が最も高いです)。std::vector<wchar_t>代わりにaを使用してください。

于 2013-01-06T17:37:02.890 に答える