将此代码与 MultiByteToWideChar 一起使用 wstring 是否安全?

Jos*_*osh 3 c++ winapi

使用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;
}
Run Code Online (Sandbox Code Playgroud)

IIn*_*ble 5

第一次调用时有一个问题MultiByteToWideChar: 不能保证字符序列以零结尾(尽管在实践中通常是这样)。将该行更改为

int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), -1, NULL, 0);
Run Code Online (Sandbox Code Playgroud)

你应该是安全的。即使MultiByteToWideChar失败并返回 0,这也是通过len在第二次调用MultiByteToWideChar.

话虽如此,它是安全的,它不会崩溃或损坏内存。然而,还有一个问题:除非输入字符串导致MultiByteToWideChar失败,否则返回的字符串将声称其size()比应有的大一个字符。我建议更改代码如下:

int len = MultiByteToWideChar(CP_UTF8, 0, in.c_str(), -1, NULL, 0);
Run Code Online (Sandbox Code Playgroud)

此实现解决了以下问题:

  • 如果输入序列不是有效的 UTF-8,它会通过传递MB_ERR_INVALID_CHARS标志来报告错误。
  • 通过抛出异常来报告错误。这使得区分转换错误和成功调用(返回零大小的字符串)成为可能。(注:std::wstringc'tor已经失败的情况下抛出异常它会感到不自然。没有抛出其他错误例外。)
  • 该实现正确处理包含嵌入NUL字符的输入。这很少使用,但是当它被使用时(例如,在组合OPENFILENAME的lpstrFilter成员时),它不会(悄悄地)因为这个原因而失败。
  • 它不会过度分配返回值的容器存储。如果cbMultiByte参数-1在对 的调用中设置为MultiByteToWideChar,则返回的长度确实包括零终止符的空间。但是,该字符归std::string实现所有,而不是要转换的字符序列的一部分。
  • 与上一个要点相关,此实现不会转换零终止符。原始代码是这样,NUL当c_str()调用成员时,返回的字符串在字符串的末尾产生 2 个字符。


Who*_*aig 5

如果你问它会起作用,可能。这是正确的吗?

  1. 你应该使用in.c_str()而不是&in[0]
  2. 您应该MultiByteToWideChar至少检查第一次的返回值。
  3. MultiByteToWideChar以 (-1) 长度调用,如果成功,将包括对零终止符的考虑(即,它总是在成功时返回 >= 1)。的长度构造函数std::wstring不需要这个。std::wstring(5,0)将为六个宽字符分配空间;5+零项。所以从技术上讲,你分配了太多的宽字符。

从MultiByteToWideChar文档开始cbMultiByte和-1:

如果此参数为 -1,则函数处理整个输入字符串,包括终止空字符。因此,生成的 Unicode 字符串有一个终止空字符,函数返回的长度包括这个字符。

  • @MooingDuck 传递输入字符串长度而不是 -1 的*非常* 好点。它不会改变我关于当 (-1) *is* 传递时 1-wchar_t 过大会发生什么的评论,但你绝对正确,应该使用 `in.length()`。现在我可以检查我的工作代码中我​​做这种事情的所有地方......谢谢你=P (2认同)