您第一次调用 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)
{
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);
MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
in.c_str(), in.size(), &out[0], out.size());
}
return out;
}
该实现解决了以下问题:
- 如果输入序列无效的UTF-8,通过传递
MB_ERR_INVALID_CHARS
标志来报告错误。
- 使用抛出异常报告错误。这使得可以区分转换错误和成功调用返回零大小字符串的情况。(注意:
std::wstring
构造函数在失败时已经抛出异常。对于其他错误不抛出异常会感觉不自然。)
- 该实现正确处理包含嵌入的
NUL
字符的输入。这很少使用,但是当它被使用时(例如,当组成OPENFILENAME的lpstrFilter成员时),它不会因此(悄悄地)失败。
- 它不会过度分配返回值的容器存储空间。如果在调用
MultiByteToWideChar
时将cbMultiByte参数设置为-1
,则返回的长度包括零终止符的空间。但是,该字符属于std::string
实现,而不是要转换的字符序列的一部分。
- 与前一个项目相关,该实现不会转换零终止符。原始代码这样做,并且当调用
c_str()
成员时,返回的字符串在末尾产生2个NUL
字符。
MultiByteToWideChar
在出现错误时会返回0。因此,剩余的代码将生成一个空字符串(而不是“垃圾”)。检查返回值是个好主意,即使在这种情况下不这样做也不会造成任何问题(除了无法区分空字符串和错误模式之外)。 - IInspectable