C++ 如何正确使用WideChartMultiByte

C++ 如何正确使用WideChartMultiByte,c++,unicode,character-encoding,codepages,C++,Unicode,Character Encoding,Codepages,我已经阅读了上的文档,但我仍然停留在这个参数上: lpMultiByteStr [out] Pointer to a buffer that receives the converted string. 我不太确定如何正确初始化变量并将其输入函数您可以通过创建一个新的字符数组来使用lpMultiByteStr[out]参数。然后传入这个字符数组以填充它。您只需要初始化字符串+1的长度,这样在转换后就可以有一个以null结尾的字符串 这里有几个有用的帮助函数,它们显示了所有参数的用法 #incl

我已经阅读了上的文档,但我仍然停留在这个参数上:

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

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

您可以通过创建一个新的字符数组来使用lpMultiByteStr[out]参数。然后传入这个字符数组以填充它。您只需要初始化字符串+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;
}
详细介绍Brian R.Bondy提供的:下面的示例说明了为什么不能简单地将输出缓冲区的大小调整为源字符串中宽字符的数量:

#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);
}

这里有两个函数(基于Brian Bondy的示例),它们使用WideCharToMultiByte和MultiByteToWideChar,使用utf8在std::WSString和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;
}

下面是一个
C
实现
WideCharToMultiByte
MultiByteToWideChar
。 在这两种情况下,我都确保将
null
字符固定到目标缓冲区的末尾

如果在没有终止空字符的情况下显式指定输入字符串长度,则MultiByteToWideChar不会以空终止输出字符串

如果在没有终止空字符的情况下显式指定输入字符串长度,则WideChartMultiByte不会以空终止输出字符串

即使有人指定了
-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;
}

代码应考虑到多字节字符字符串中所需的字节数可能大于宽字符串中的字符数。单个宽字符可能会导致多字节字符字符串中包含2个或更多字节,具体取决于所涉及的编码。例如,我们会想到亚洲字符,但这实际上取决于用于转换的代码页。在您的示例中,这可能不是问题,因为任何非ANSI字符都将替换为问号。若要获取转换所需的大小,请调用WideChartMultiByte,将0作为目标缓冲区的大小。然后,它将返回目标缓冲区大小所需的字节数。是否有一种可移植的方法,即POSIX,可以这样做?WideChartMultiByte是一个Windows函数。当使用gb2312之类的代码时,字节数或宽字符数计数将与此代码中断。这是代码页/编码转换中一个重要且经常被忽略的方面的极好示例-1 OP请求有关lpMultiByteStr参数的帮助。这个答案没有回答OP,它与另一个发布的答案相切。@Error454:他们在2008年没有评论。只需标记它。+1表示排除null,返回的sizeRequired包含null的空间,因此lpMultiByteStr的正确初始化必须考虑到这一点。是否有任何原因导致您似乎提出问题但不接受回答?在这些网站上,奖励好的答案通常是一种很好的做法,因为人们在回答你的问题时投入了大量的时间,并给予反馈。下面是一些非常好的答案。。。(轻推)应该注意的是,在C++11之前,std::string和std::wstring不能保证它们的内存是连续的。我严重怀疑是否有一个商用stl实现没有连续向量。第一个C++规范中不需要连续内存的事实是一个疏忽:“TimNiga以前的注释是关于字符串,而不是向量。在C++98中,字符串不能保证是连续的(不是Sutter提到的结果),尽管所有的实际实现都是如此。@Swift C_str()保证返回指向连续缓冲区的指针,但在C++11之前,这不能保证与字符串的内部表示形式相同。这如何处理非英语字母,如斯堪的纳维亚字母?据我所见,它变得乱七八糟-(
// 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;
}
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;
}