如何正确使用WideCharToMultiByte

问题描述 投票:0回答:5

我已阅读有关 WideCharToMultiByte 的文档,但我陷入了这个参数:

lpMultiByteStr
[out] Pointer to a buffer that receives the converted string.

我不太确定如何正确初始化变量并将其输入函数

c++ unicode character-encoding codepages
5个回答
155
投票

这里有几个函数(基于 Brian Bondy 的示例),它们使用 WideCharToMultiByte 和 MultiByteToWideChar 使用 utf8 在 std::wstring 和 std::string 之间进行转换,以免丢失任何数据。

// Convert a wide Unicode string to an UTF8 string
std::string utf8_encode(const std::wstring &wstr)
{
    if( wstr.empty() ) return std::string();
    int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
    std::string strTo( size_needed, 0 );
    WideCharToMultiByte                  (CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
    return strTo;
}

// Convert an UTF8 string to a wide Unicode String
std::wstring utf8_decode(const std::string &str)
{
    if( str.empty() ) return std::wstring();
    int size_needed = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), NULL, 0);
    std::wstring wstrTo( size_needed, 0 );
    MultiByteToWideChar                  (CP_UTF8, 0, &str[0], (int)str.size(), &wstrTo[0], size_needed);
    return wstrTo;
}

40
投票

详细阐述 Brian R. Bondy 提供的 answer:下面的示例说明了为什么不能简单地将输出缓冲区的大小调整为源字符串中的宽字符数:

#include <windows.h>
#include <stdio.h>
#include <wchar.h>
#include <string.h>

/* string consisting of several Asian characters */
wchar_t wcsString[] = L"\u9580\u961c\u9640\u963f\u963b\u9644";

int main() 
{

    size_t wcsChars = wcslen( wcsString);

    size_t sizeRequired = WideCharToMultiByte( 950, 0, wcsString, -1, 
                                               NULL, 0,  NULL, NULL);

    printf( "Wide chars in wcsString: %u\n", wcsChars);
    printf( "Bytes required for CP950 encoding (excluding NUL terminator): %u\n",
             sizeRequired-1);

    sizeRequired = WideCharToMultiByte( CP_UTF8, 0, wcsString, -1,
                                        NULL, 0,  NULL, NULL);
    printf( "Bytes required for UTF8 encoding (excluding NUL terminator): %u\n",
             sizeRequired-1);
}

输出:

Wide chars in wcsString: 6
Bytes required for CP950 encoding (excluding NUL terminator): 12
Bytes required for UTF8 encoding (excluding NUL terminator): 18

21
投票

您可以通过创建新的字符数组来使用 lpMultiByteStr [out] 参数。 然后传入这个 char 数组来填充它。 只需要初始化字符串的长度+1,这样转换后就可以得到一个以null结尾的字符串。

这里有一些对您有用的辅助函数,它们显示了所有参数的用法。

#include <string>

std::string wstrtostr(const std::wstring &wstr)
{
    // Convert a Unicode string to an ASCII string
    std::string strTo;
    char *szTo = new char[wstr.length() + 1];
    szTo[wstr.size()] = '\0';
    WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, szTo, (int)wstr.length(), NULL, NULL);
    strTo = szTo;
    delete[] szTo;
    return strTo;
}

std::wstring strtowstr(const std::string &str)
{
    // Convert an ASCII string to a Unicode String
    std::wstring wstrTo;
    wchar_t *wszTo = new wchar_t[str.length() + 1];
    wszTo[str.size()] = L'\0';
    MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, wszTo, (int)str.length());
    wstrTo = wszTo;
    delete[] wszTo;
    return wstrTo;
}

--

在文档中的任何时候,当您看到它有一个指向类型的指针的参数,并且他们告诉您这是一个输出变量时,您将需要创建该类型,然后传入指向它的指针。 该函数将使用该指针来填充您的变量。

这样你就可以更好地理解这一点:

//pX is an out parameter, it fills your variable with 10.
void fillXWith10(int *pX)
{
  *pX = 10;
}

int main(int argc, char ** argv)
{
  int X;
  fillXWith10(&X);
  return 0;
}

1
投票

这是

C
WideCharToMultiByte
MultiByteToWideChar
实现。 在这两种情况下,我都会确保将
null
字符添加到目标缓冲区的末尾。

如果显式指定输入字符串长度而没有终止空字符,则 MultiByteToWideChar 不会以空终止输出字符串。

还有

如果显式指定输入字符串长度而没有终止空字符,则 WideCharToMultiByte 不会以空终止输出字符串。

即使有人指定

-1
并传入
null
终止的字符串,我仍然为额外的
null
字符分配足够的空间,因为对于我的用例来说,这不是问题。

wchar_t* utf8_decode( const char* str, int nbytes ) {    
    int nchars = 0;
    if ( ( nchars = MultiByteToWideChar( CP_UTF8, 
        MB_ERR_INVALID_CHARS, str, nbytes, NULL, 0 ) ) == 0 ) {
        return NULL;
    }

    wchar_t* wstr = NULL;
    if ( !( wstr = malloc( ( ( size_t )nchars + 1 ) * sizeof( wchar_t ) ) ) ) {
        return NULL;
    }

    wstr[ nchars ] = L'\0';
    if ( MultiByteToWideChar( CP_UTF8, MB_ERR_INVALID_CHARS, 
        str, nbytes, wstr, ( size_t )nchars ) == 0 ) {
        free( wstr );
        return NULL;
    }
    return wstr;
} 


char* utf8_encode( const wchar_t* wstr, int nchars ) {
    int nbytes = 0;
    if ( ( nbytes = WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, 
        wstr, nchars, NULL, 0, NULL, NULL ) ) == 0 ) {
        return NULL;
    }

    char* str = NULL;
    if ( !( str = malloc( ( size_t )nbytes + 1 ) ) ) {
        return NULL;
    }

    str[ nbytes ] = '\0';
    if ( WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, 
        wstr, nchars, str, nbytes, NULL, NULL ) == 0 ) {
        free( str );
        return NULL;
    }
    return str;
}

0
投票

我正在使用这两个辅助函数:

#pragma once
#include <Windows.h>
#include <string>
#include <string_view>

template<bool ErrCode = false>
std::conditional_t<!ErrCode, std::wstring, std::pair<std::wstring, DWORD>> multiByteToWideChar( std::string_view mbStr, UINT CodePage = CP_THREAD_ACP, DWORD dwFlags = 0 )
{
    using namespace std;
    constexpr char const *THROW_STR = "MultiByteToWideChar() conversion failed";
    auto call = [&]( wchar_t *str, size_t len ) { return (unsigned)MultiByteToWideChar( CodePage, dwFlags, mbStr.data(), mbStr.length(), str, (int)len ); };
    size_t length = call( (LPWSTR)L"", 0 );
    if( DWORD dwErr; length == 0 && (dwErr = GetLastError()) != NO_ERROR )
        if constexpr( !ErrCode )
            throw system_error( dwErr, system_category(), THROW_STR );
        else
            return { wstring(), dwErr };
    wstring wstr( length, L'\0' );
    size_t written = call( wstr.data(), length );
    if( written != length )
        if( written == 0 )
            if constexpr( !ErrCode )
                throw system_error( GetLastError(), system_category(), THROW_STR );
            else
                return { wstring(), GetLastError() };
        else
            throw invalid_argument( THROW_STR );
    if( length )
        wstr.resize( length - 1 );
    if constexpr( !ErrCode )
        return wstr;
    else
        return { wstr, NO_ERROR };
}

template<bool ErrCode = false>
std::conditional_t<!ErrCode, std::string, std::pair<std::string, DWORD>> wideCharToMultiByte( std::wstring_view wideCharStr, UINT CodePage = CP_THREAD_ACP, DWORD dwFlags =  0, LPCCH lpDefaultChar = nullptr, LPBOOL lpUsedDefaultChar = nullptr )
{
    using namespace std;
    constexpr char const *THROW_STR = "WideCharToMultiByte() conversion failed";
    auto call = [&]( char *str, size_t length ) { return (unsigned)WideCharToMultiByte( CodePage, dwFlags, wideCharStr.data(), (int)wideCharStr.length(), str, (int)length, lpDefaultChar, lpUsedDefaultChar ); };
    size_t length = call( (LPSTR)"", 0 );
    if( DWORD dwErr; length == 0 && (dwErr = GetLastError()) != NO_ERROR )
        if constexpr( !ErrCode )
            throw system_error( dwErr, system_category(), THROW_STR );
        else
            return { string(), dwErr };
    string str( length, '\0' );
    size_t written = call( str.data(), length );
    if( written != length )
        if( written == 0 )
            if constexpr( !ErrCode )
                throw system_error( GetLastError(), system_category(), THROW_STR );
            else
                return { string(), GetLastError() };
        else
            throw invalid_argument( THROW_STR );
    if( length )
        str.resize( length - 1 );
    if constexpr( !ErrCode )
        return str;
    else
        return { str, NO_ERROR };
}
© www.soinside.com 2019 - 2024. All rights reserved.