【问题标题】:How to convert between widecharacter and multi byte character string in windows?如何在 Windows 中的宽字符和多字节字符串之间进行转换?
【发布时间】:2017-08-05 05:54:16
【问题描述】:

我有一个字符串类型为WCHAR* 的Windows 应用程序。我需要将其转换为 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;
}

【问题讨论】:

  • 为什么您觉得需要重新编写其他代码,而不是按原样使用?显然,您还没有阅读正在使用的功能的文档。而且您确实意识到您不能使用std:cout 输出WCHAR*,不是吗?您需要改用std::wcout
  • 当使用cout 输出WCHAR* 时,你期望看到什么?
  • 我可以。唯一的原因是它使用 std::string 和 std::wstring 的代码。这对我来说是另一个层次的间接性。因为代码的所有其他部分都使用 char* 和 WCHAR*。
  • @Appu:好吧,那您并没有真正利用 C++,是吗?你可能只用 C 编写代码。
  • 那么如果系统很大程度上基于char*/wchar_t*呢?这就是std::string::c_str()std::wstring::c_str() 的用途。让 STL 为您处理内存管理(尤其是因为无论如何您都搞错了)。

标签: c++ windows unicode


【解决方案1】:

在 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 是多么容易。

【讨论】:

  • _bstr_t::_bstr_t(const char*) 上的文档不明确:此构造函数首先执行多字节到 Unicode 的转换。。它使用哪种编码从多字节转换为 Unicode?​​span>
  • 我假设它将使用 OEM 编码而不是 UTF8。并且 OP 正在转换为/从 UTF8 字符串。
  • @Paul 我怀疑它使用 OEM 代码页,它可能使用“ANSI”代码页 (CP_ACP)。
  • @Anders 在这种情况下没关系,它仍然不能解决 OP 的任务(将 UTF8 字符串转换为 UTF16 字符串/从 UTF16 字符串转换)。
  • _bstr_t 不是一般的 Windows 解决方案,它是 Visual Studio 特定的解决方案。其他 Windows 编译器可能没有它。更糟糕的是,由于它包装了一个 BSTR,它使用 COM 内存管理器而不是编译器自己的 RTL 内存管理器。然后它必须被复制到std::wstring,所以你可能正在使用混乱代码,但你正在做双重分配。
【解决方案2】:

你的转换函数有问题。

MultiByteToWideChar() 的返回值是宽字符数,而不是您当前处理的字节数。调用malloc()时需要将值乘以sizeof(WCHAR)

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

cbMultiByte [输入]
lpMultiByteStr 参数指示的字符串的大小(以字节为单位)。或者,如果字符串以 null 结尾,则可以将此参数设置为 -1。请注意,如果cbMultiByte 为0,则函数失败。

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

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

...

返回值

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

您没有以空值终止您的输出字符串。

convert_from_wstring() 函数也是如此。 Read the WideCharToMultiByte() documentation:

cchWideChar [输入]
lpWideCharStr 指示的字符串的大小(以字符为单位)。或者,如果字符串以 null 结尾,则可以将此参数设置为 -1。如果cchWideChar设置为0,则函数失败。

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

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

...

返回值

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

话虽如此,您的main() 代码正在泄漏分配的字符串。由于它们是使用malloc() 分配的,因此在使用完它们后,您需要使用free() 释放它们:

此外,您不能将WCHAR* 字符串传递给std::cout。好吧,你可以,但它没有用于宽字符串输入的operator&lt;&lt;,但它确实有用于void* 输入的operator&lt;&lt;,所以它最终只会输出内存地址 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* 输入的构造函数和可用于char* 输出的c_str() 方法的其他代码进行交互,std::wstringwchar_t* 也是如此。

【讨论】:

  • 有没有人对wstring_convertMultiByteToWideChar()WideCharToMultiByte() 的最新实现进行性能比较?
  • C++11 示例让我免于数小时的挫败感……为 Remy 致敬。但是我发现了另一个带有一些错误处理的“版本”,也许你想把它添加到你的答案中?我把它放在pastebin上:pastebin.com/hq2grb84
猜你喜欢
  • 2017-02-18
  • 1970-01-01
  • 1970-01-01
  • 2017-04-16
  • 1970-01-01
  • 2017-09-06
  • 1970-01-01
  • 1970-01-01
  • 2011-08-25
相关资源
最近更新 更多