如何在Windows中转换宽字符和多字节字符串?

时间:2017-03-14 18:24:28

标签: c++ windows unicode

我有一个Windows应用程序,其中字符串类型为WCHAR*。我需要将其转换为char*以传入C API。我正在使用MultiByteToWideCharWideCharToMultiByte函数来执行转换。

但由于某种原因,转换不合适。我在输出中看到很多胡言乱语。以下代码是this stackoverflow应答中的修改版本。

WCHAR* convert_to_wstring(const char* str)
{
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, str, (int)strlen(str), NULL, 0);
    WCHAR* wstrTo = (WCHAR*)malloc(size_needed);
    MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)strlen(str), wstrTo, size_needed);
    return wstrTo;
}

char* convert_from_wstring(const WCHAR* wstr)
{
    int size_needed = WideCharToMultiByte(CP_UTF8, 0, wstr, (int)wcslen(wstr), NULL, 0, NULL, NULL);
    char* strTo = (char*)malloc(size_needed);
    WideCharToMultiByte(CP_UTF8, 0, wstr, (int)wcslen(wstr), strTo, size_needed, NULL, NULL);
    return strTo;
}

int main()
{
    const WCHAR* wText = L"Wide string";
    const char* text = convert_from_wstring(wText);
    std::cout << text << "\n";
    std::cout << convert_to_wstring("Multibyte string") << "\n";
    return 0;
}

2 个答案:

答案 0 :(得分:12)

您的转换功能有问题。

MultiByteToWideChar()的返回值是宽字符数,而非字节数,就像您当前正在处理它一样。调用sizeof(WCHAR)时,您需要将malloc()的值加倍。

您也没有考虑返回值不包含空终止符的空格,因为您没有在-1参数中传递cbMultiByteRead the MultiByteToWideChar() documentation

  

cbMultiByte [in]
  由lpMultiByteStr参数指示的字符串的大小(以字节为单位)。或者,如果字符串以空值终止,则此参数可以设置为-1。请注意,如果cbMultiByte为0,则函数将失败。

     

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

     

如果此参数设置为正整数,则该函数将精确处理指定的字节数。 如果提供的大小不包含终止空字符,则生成的Unicode字符串不以空值终止,并且返回的长度不包括此字符。

     

...

     

返回值

     

如果成功,则返回写入lpWideCharStr指示的缓冲区的字符数。如果函数成功且cchWideChar为0,则返回值为lpWideCharStr指示的缓冲区所需的大小,字符

您不是以null结尾输出字符串。

您的convert_from_wstring()功能也是如此。 Read the WideCharToMultiByte() documentation

  

cchWideChar [in]
  由lpWideCharStr表示的字符串的大小(以字符为单位)。或者,如果字符串以空值终止,则此参数可以设置为-1。如果cchWideChar设置为0,则函数失败。

     

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

     

如果此参数设置为正整数,则该函数将精确处理指定数量的字符。 如果提供的大小不包含终止空字符,则生成的字符串不以空值终止,并且返回的长度不包括此字符

     

...

     

返回值

     

如果成功,则返回写入lpMultiByteStr指向的缓冲区的字节数。如果函数成功且cbMultiByte为0,则返回值为lpMultiByteStr指示的缓冲区所需的大小,以字节为单位

话虽如此,您的main()代码正在泄漏已分配的字符串。由于它们已分配malloc(),因此您需要在使用free()后将其取消分配:

此外,您无法将WCHAR*字符串传递给std::cout。嗯,你可以,但它没有operator<<用于宽字符串输入,但它确实有一个operator<<用于void*输入,所以它最终会输出内存地址 WCHAR*指向的是,而不是实际的字符。如果要输出宽字符串,请改用std::wcout

尝试更像这样的事情:

WCHAR* convert_to_wstring(const char* str)
{
    int str_len = (int) strlen(str);
    int num_chars = MultiByteToWideChar(CP_UTF8, 0, str, str_len, NULL, 0);
    WCHAR* wstrTo = (WCHAR*) malloc((num_chars + 1) * sizeof(WCHAR));
    if (wstrTo)
    {
        MultiByteToWideChar(CP_UTF8, 0, str, str_len, wstrTo, num_chars);
        wstrTo[num_chars] = L'\0';
    }
    return wstrTo;
}

CHAR* convert_from_wstring(const WCHAR* wstr)
{
    int wstr_len = (int) wcslen(wstr);
    int num_chars = WideCharToMultiByte(CP_UTF8, 0, wstr, wstr_len, NULL, 0, NULL, NULL);
    CHAR* strTo = (CHAR*) malloc((num_chars + 1) * sizeof(CHAR));
    if (strTo)
    {
        WideCharToMultiByte(CP_UTF8, 0, wstr, wstr_len, strTo, num_chars, NULL, NULL);
        strTo[num_chars] = '\0';
    }
    return strTo;
}

int main()
{
    const WCHAR* wText = L"Wide string";
    const char* text = convert_from_wstring(wText);
    std::cout << text << "\n";
    free(text);

    const WCHAR *wtext = convert_to_wstring("Multibyte string");
    std::wcout << wtext << "\n";
    free(wtext);

    return 0;
}

话虽如此,您确实应该使用std::stringstd::wstring代替char*wchar_t*来改善内存管理:

std::wstring convert_to_wstring(const std::string &str)
{
    int num_chars = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
    std::wstring wstrTo;
    if (num_chars)
    {
        wstrTo.resize(num_chars);
        MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &wstrTo[0], num_chars);
    }
    return wstrTo;
}

std::string convert_from_wstring(const std::wstring &wstr)
{
    int num_chars = WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.length(), NULL, 0, NULL, NULL);
    std::string strTo;
    if (num_chars > 0)
    {
        strTo.resize(num_chars);
        WideCharToMultiByte(CP_UTF8, 0, wstr.c_str(), wstr.length(), &strTo[0], num_chars, NULL, NULL);
    }
    return strTo;
}

int main()
{
    const WCHAR* wText = L"Wide string";
    const std::string text = convert_from_wstring(wText);
    std::cout << text << "\n";

    const std::wstring wtext = convert_to_wstring("Multibyte string");
    std::wcout << wtext << "\n";

    return 0;
}

如果您使用的是C ++ 11或更高版本,请查看std::wstring_convert类以便在UTF字符串之间进行转换,例如:

std::wstring convert_to_wstring(const std::string &str)
{
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conv;
    return conv.from_bytes(str);
}

std::string convert_from_wstring(const std::wstring &wstr)
{
    std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t> conv;
    return conv.to_bytes(wstr);
}

如果您需要与基于char* / wchar_t*的其他代码进行互动,std::string作为接受char*输入的构造函数和c_str()可用于char*输出的方法,std::wstringwchar_t*也是如此。

答案 1 :(得分:0)

在Windows上执行此操作的最简单方法是使用

中的_bstr_
#include <comdef.h> // _bstr_t

#include <string>
#include <iostream>

std::wstring convert_to_wstring(const char* str)
{
    return _bstr_t(str);
}

std::string convert_from_wstring(const WCHAR* wstr)
{
    return _bstr_t(wstr);
}

int main()
{
    const auto text = convert_from_wstring(L"Wide string");
    const auto wide_text = convert_to_wstring("Multibyte string");
    return 0;
}

另外,请注意返回std::wstringstd::string会更容易。