への最初の呼び出しに問題があります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
文字を含む入力を適切に処理します。これはめったに使用されませんが、使用される場合 (たとえば、OPENFILENAMEのlpstrFilterメンバーを構成する場合)、その理由で (静かに) 失敗することはありません。
- 戻り値のコンテナー ストレージが過剰に割り当てられることはありません。への呼び出しでcbMultiByte引数が に設定されている場合、返される長さにはゼロ ターミネータのスペースが含まれます。ただし、この文字は実装によって所有され、変換される文字シーケンスの一部ではありません。
-1
MultiByteToWideChar
std::string
- 前の箇条書きに関連して、この実装はゼロ ターミネータを変換しません。元のコードはそうで、返された文字列は、メンバーが呼び出され
NUL
たときに文字列の末尾に 2 文字を生成します。c_str()