如何使用MultiByteToWideChar?

36
我想将一个普通的 string 转换成 wstring。为此,我试图使用 Windows API 函数 MultiByteToWideChar。但是它对我不起作用。
这是我做的事情:
string x = "This is c++ not java";
wstring Wstring;
MultiByteToWideChar( CP_UTF8 , 0 , x.c_str() , x.size() , &Wstring , 0 ); 
最后一行代码会产生编译错误:
'MultiByteToWideChar' : cannot convert parameter 5 from 'std::wstring *' to 'LPWSTR'

如何修复这个错误?

另外,参数cchWideChar的值应该是多少?0可以吗?


1
你不能将指向 std::wstring 的指针传递给此函数。 - Cat Plus Plus
5个回答

60

必须调用MultiByteToWideChar两次:

  1. 第一次调用MultiByteToWideChar用于查找你需要的宽字符串的缓冲区大小。参考Microsoft文档;文档指出:

    如果函数成功并且cchWideChar为0,则返回值是由lpWideCharStr指示的缓冲区所需的字符数。

    因此,要使MultiByteToWideChar给出所需的大小,将最后一个参数cchWideChar的值设置为0。你还应该将倒数第二个参数lpWideCharStr设置为NULL

  2. 使用前一步骤的缓冲区大小获取非常量缓冲区以容纳宽字符串。将此缓冲区传递到另一个MultiByteToWideChar调用中。这次,最后一个参数应该是缓冲区的实际大小,而不是0。

一个简单的例子:

int wchars_num = MultiByteToWideChar( CP_UTF8 , 0 , x.c_str() , -1, NULL , 0 );
wchar_t* wstr = new wchar_t[wchars_num];
MultiByteToWideChar( CP_UTF8 , 0 , x.c_str() , -1, wstr , wchars_num );
// do whatever with wstr
delete[] wstr;

注意,cbMultiByte参数使用-1。这将使得生成的字符串以空字符结尾,省去了处理它们的麻烦。


5
强调需要调用MultiByteToWideChar两次,这对于字符集转换函数是至关重要的。+1 - Stephan
@ eran,wchar_t*LPTSTR 有什么区别? - Suhail Gupta
@Suhail Gupta,如果您正在使用Unicode编译,则完全相同。在多字节构建中,LPTSTR会扩展为常规的“ char *”。使用这些宏允许您创建Unicode和非Unicode版本。虽然我现在想不出使用这种方法的原因,但由于Unicode现在是VS的默认设置,请使用其中任何一个。 - Eran
哎呀!不存在所谓的 free[],即使存在,我也绝不会支持这样的代码。请使用适当调整大小的 std::vector<wchar_t> - Puppy
1
@eran:那个令人不舒服的部分不是 free[],而是直接使用 newdelete ,请求泄漏资源、溢出缓冲区并破坏堆的“PLEASE LEAK RESOURCES AND OVERFLOW MY BUFFERS AND CORRUPT MY HEAP”。请使用 std::vector<wchar_t> - Puppy
显示剩余4条评论

13

常见的单位转换:

#define WIN32_LEAN_AND_MEAN

#include <Windows.h>

#include <string>

std::string ConvertWideToANSI(const std::wstring& wstr)
{
    int count = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), wstr.length(), NULL, 0, NULL, NULL);
    std::string str(count, 0);
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &str[0], count, NULL, NULL);
    return str;
}

std::wstring ConvertAnsiToWide(const std::string& str)
{
    int count = MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), NULL, 0);
    std::wstring wstr(count, 0);
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length(), &wstr[0], count);
    return wstr;
}

std::string ConvertWideToUtf8(const std::wstring& wstr)
{
    int count = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.length(), NULL, 0, NULL, NULL);
    std::string str(count, 0);
    WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), -1, &str[0], count, NULL, NULL);
    return str;
}

std::wstring ConvertUtf8ToWide(const std::string& str)
{
    int count = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
    std::wstring wstr(count, 0);
    MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &wstr[0], count);
    return wstr;
}

2
您可以尝试下面的解决方案。我测试过,它可以检测到特殊字符(例如:º ä ç á),并且适用于Windows XP、Windows 2000 SP4及更高版本、Windows 7、8、8.1和10。使用std::wstring代替new wchar_t/delete,我们可以减少泄漏资源、溢出缓冲区和堆损坏等问题。
dwFlags设置为MB_ERR_INVALID_CHARS可在Windows 2000 SP4及更高版本、Windows XP上运行。如果不设置此标志,函数会悄悄地删除非法代码点。
std::wstring ConvertStringToWstring(const std::string &str)
{
    if (str.empty())
    {
        return std::wstring();
    }
    int num_chars = MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, str.c_str(), str.length(), NULL, 0);
    std::wstring wstrTo;
    if (num_chars)
    {
        wstrTo.resize(num_chars);
        if (MultiByteToWideChar(CP_ACP, MB_ERR_INVALID_CHARS, str.c_str(), str.length(), &wstrTo[0], num_chars))
        {
            return wstrTo;
        }
    }
    return std::wstring();
}

1

关于这个问题,我有第二个问题要问!

WideCharToMultiByte() 和 MultiByteToWideChar() 很难使用。每次转换都需要两次调用例程,并且您必须负责分配/释放内存并确保字符串正确终止。您需要一个包装器!

我在我的博客上有一个方便的 C++ 包装器,在这里,欢迎您使用。

这是今天早上的另一个 问题


-1
该函数不能接受 C++ 字符串的指针。它需要一个足够大的宽字符缓冲区的指针 - 您必须自己分配此缓冲区。
string x = "This is c++ not java";
wstring Wstring;
Wstring.resize(x.size());
int c =  MultiByteToWideChar( CP_UTF8 , 0 , x.c_str() , x.size() , &Wstring[0], 0 ); 

1
MultiByteToWideChar 需要一个 wchar_t* 类型的参数。wstringstd::wstring 类型,所以它不能被传递给 MultiByteToWideChar(甚至不是它的指针)。但好消息是,std::wstring 内部将其数据存储为 wchar_t* 并提供两个函数来访问此内部数据:data()(在此处使用)和 c_str() - Stephan
1
@DeadMG,需要注意的是wstring.data()返回一个const wchar_t*,根据cplusplus.com的说法,它不应该被直接修改(你可能比我更清楚这样做的影响)。另一方面,由于MBTWC的最后一个参数是0,因此在那个缓冲区里不会放置任何内容... - Eran
@eran:哎呀,你说得对,返回值应该是“const”。 - Puppy
无法以那种方式工作,wstring使用32位字符,而win32使用16位Unicode字符... - gabry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接