您第一次调用 有一个问题MultiByteToWideChar
:不能保证字符序列以零结尾(尽管实际上通常是这样)。将该行更改为
int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), -1, NULL, 0);
你应该是安全的。即使MultiByteToWideChar
失败并返回 0,这也是通过len
在第二次调用中作为最终参数传递来解释的MultiByteToWideChar
。
话虽如此,它是安全的,因为它不会崩溃或损坏内存。然而,还有一个问题:除非输入字符串导致MultiByteToWideChar
失败,否则返回的字符串将声称它size()
比它应该的大一个字符。我建议按如下方式更改代码:
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;
}
此实现解决了以下问题:
- 如果输入序列不是有效的 UTF-8,它会通过传递
MB_ERR_INVALID_CHARS
标志来报告错误。
- 通过抛出异常来报告错误。这使得区分转换错误和成功调用(返回零大小的字符串)成为可能。(注意:
std::wstring
c'tor 已经在失败的情况下抛出异常。对于其他错误不抛出异常会感觉不自然。 )
- 该实现正确处理包含嵌入
NUL
字符的输入。这很少使用,但是当它使用时(例如,在组成OPENFILENAME的lpstrFilter成员时),它不会因此(默默地)失败。
- 它不会过度分配返回值的容器存储。如果cbMultiByte参数
-1
在对 的调用中设置为MultiByteToWideChar
,则返回的长度确实包含零终止符的空间。但是,此字符由std::string
实现拥有,而不是要转换的字符序列的一部分。
- 与上一个要点相关,此实现不转换零终止符。原始代码确实如此,
NUL
当c_str()
调用成员时,返回的字符串在字符串末尾产生 2 个字符。