c++ - 将 UTF-16 转换为 UTF-8

标签 c++ windows winapi unicode utf

我目前使用的是 VC++ 2008 MFC。由于 PostgreSQL 不支持 UTF-16(Windows 为 Unicode 使用的编码),我需要在存储之前将字符串从 UTF-16 转换为 UTF-8。

这是我的代码片段。

// demo.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include "demo.h"
#include "Utils.h"
#include <iostream>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// The one and only application object

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    // initialize MFC and print and error on failure
    if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
    {
        // TODO: change error code to suit your needs
        _tprintf(_T("Fatal Error: MFC initialization failed\n"));
        nRetCode = 1;
    }
    else
    {
        // TODO: code your application's behavior here.
    }

    CString utf16 = _T("Hello");
    std::cout << utf16.GetLength() << std::endl;
    CStringA utf8 = UTF8Util::ConvertUTF16ToUTF8(utf16);
    std::cout << utf8.GetLength() << std::endl;
    getchar();
    return nRetCode;
}

和转换函数。

namespace UTF8Util
{
//----------------------------------------------------------------------------
// FUNCTION: ConvertUTF8ToUTF16
// DESC: Converts Unicode UTF-8 text to Unicode UTF-16 (Windows default).
//----------------------------------------------------------------------------
CStringW ConvertUTF8ToUTF16( __in const CHAR * pszTextUTF8 )
{
    //
    // Special case of NULL or empty input string
    //
    if ( (pszTextUTF8 == NULL) || (*pszTextUTF8 == '\0') )
    {
        // Return empty string
        return L"";
    }

    //
    // Consider CHAR's count corresponding to total input string length,
    // including end-of-string (\0) character
    //
    const size_t cchUTF8Max = INT_MAX - 1;
    size_t cchUTF8;
    HRESULT hr = ::StringCchLengthA( pszTextUTF8, cchUTF8Max, &cchUTF8 );

    if ( FAILED( hr ) )
    {
        AtlThrow( hr );
    }

    // Consider also terminating \0
    ++cchUTF8;

    // Convert to 'int' for use with MultiByteToWideChar API
    int cbUTF8 = static_cast<int>( cchUTF8 );

    //
    // Get size of destination UTF-16 buffer, in WCHAR's
    //
    int cchUTF16 = ::MultiByteToWideChar(
        CP_UTF8,                // convert from UTF-8
        MB_ERR_INVALID_CHARS,   // error on invalid chars
        pszTextUTF8,            // source UTF-8 string
        cbUTF8,                 // total length of source UTF-8 string,
                                // in CHAR's (= bytes), including end-of-string \0
        NULL,                   // unused - no conversion done in this step
        0                       // request size of destination buffer, in WCHAR's
        );

    ATLASSERT( cchUTF16 != 0 );

    if ( cchUTF16 == 0 )
    {
        AtlThrowLastWin32();
    }

    //
    // Allocate destination buffer to store UTF-16 string
    //
    CStringW strUTF16;
    WCHAR * pszUTF16 = strUTF16.GetBuffer( cchUTF16 );

    //
    // Do the conversion from UTF-8 to UTF-16
    //

    int result = ::MultiByteToWideChar(
        CP_UTF8,                // convert from UTF-8
        MB_ERR_INVALID_CHARS,   // error on invalid chars
        pszTextUTF8,            // source UTF-8 string
        cbUTF8,                 // total length of source UTF-8 string,
                                // in CHAR's (= bytes), including end-of-string \0
        pszUTF16,               // destination buffer
        cchUTF16                // size of destination buffer, in WCHAR's
        );

    ATLASSERT( result != 0 );

    if ( result == 0 )
    {
        AtlThrowLastWin32();
    }

    // Release internal CString buffer
    strUTF16.ReleaseBuffer();

    // Return resulting UTF16 string
    return strUTF16;
}

//----------------------------------------------------------------------------
// FUNCTION: ConvertUTF16ToUTF8
// DESC: Converts Unicode UTF-16 (Windows default) text to Unicode UTF-8.
//----------------------------------------------------------------------------
CStringA ConvertUTF16ToUTF8( __in const WCHAR * pszTextUTF16 )
{
    //
    // Special case of NULL or empty input string
    //
    if ( (pszTextUTF16 == NULL) || (*pszTextUTF16 == L'\0') )
    {
        // Return empty string
        return "";
    }

    //
    // Consider WCHAR's count corresponding to total input string length,
    // including end-of-string (L'\0') character.
    //
    const size_t cchUTF16Max = INT_MAX - 1;
    size_t cchUTF16;
    HRESULT hr = ::StringCchLengthW( pszTextUTF16, cchUTF16Max, &cchUTF16 );

    if ( FAILED( hr ) )
    {
        AtlThrow( hr );
    }

    // Consider also terminating \0
    ++cchUTF16;

    //
    // WC_ERR_INVALID_CHARS flag is set to fail if invalid input character
    // is encountered.
    // This flag is supported on Windows Vista and later.
    // Don't use it on Windows XP and previous.
    //

#if (WINVER >= 0x0600)
    DWORD dwConversionFlags = WC_ERR_INVALID_CHARS;
#else
    DWORD dwConversionFlags = 0;
#endif

    //
    // Get size of destination UTF-8 buffer, in CHAR's (= bytes)
    //
    int cbUTF8 = ::WideCharToMultiByte(
        CP_UTF8,                // convert to UTF-8
        dwConversionFlags,      // specify conversion behavior
        pszTextUTF16,           // source UTF-16 string
        static_cast<int>( cchUTF16 ),   // total source string length, in WCHAR's,
                                        // including end-of-string \0
        NULL,                   // unused - no conversion required in this step
        0,                      // request buffer size
        NULL, NULL              // unused
        );

    ATLASSERT( cbUTF8 != 0 );

    if ( cbUTF8 == 0 )
    {
        AtlThrowLastWin32();
    }

    //
    // Allocate destination buffer for UTF-8 string
    //
    CStringA strUTF8;
    int cchUTF8 = cbUTF8; // sizeof(CHAR) = 1 byte
    CHAR * pszUTF8 = strUTF8.GetBuffer( cchUTF8 );

    //
    // Do the conversion from UTF-16 to UTF-8
    //
    int result = ::WideCharToMultiByte(
        CP_UTF8,                // convert to UTF-8
        dwConversionFlags,      // specify conversion behavior
        pszTextUTF16,           // source UTF-16 string
        static_cast<int>( cchUTF16 ),   // total source string length, in WCHAR's,
                                        // including end-of-string \0
        pszUTF8,                // destination buffer
        cbUTF8,                 // destination buffer size, in bytes
        NULL, NULL              // unused
        ); 

    ATLASSERT( result != 0 );

    if ( result == 0 )
    {
        AtlThrowLastWin32();
    }

    // Release internal CString buffer
    strUTF8.ReleaseBuffer();

    // Return resulting UTF-8 string
    return strUTF8;
}

} // namespace UTF8Util

但是,在运行时,我在

得到了异常

ATLASSERT( cbUTF8 != 0 );

在尝试获取目标 UTF-8 缓冲区的大小时

  1. 我错过了什么?
  2. 如果我使用中文字符进行测试,我如何验证生成的 UTF-8 字符串是否正确?

最佳答案

您还可以使用 ATL String Conversion Macros - 要从 UTF-16 转换为 UTF-8,请使用 CW2A 并传递 CP_UTF8 作为代码页,例如:

CW2A utf8(buffer, CP_UTF8);
const char* data = utf8.m_psz;

关于c++ - 将 UTF-16 转换为 UTF-8,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3082620/

相关文章:

c++ - Qt 调试:如何知道 QCoreApplication::notifyInternal2 向哪个对象发送消息?

c++ - 在 vector 的 vector 上捕获 out_of_range

在 Windows 核心文件上调用 fopen 返回 NULL 指针

来自命令提示符的 Windows 8 通知服务通知

c++ - wglCreateContextAttribsARB 在 NVIDIA 硬件上失败

c++ - 对象看似删除自身或内存在构造函数后发生变化

c++ - Boost 测试框架是否支持测试依赖?

android - 如何使用 Android SDK 管理器安装 NDK

c++ - 从内存中绘制位图

c++ - WINAPI ReadProcessMemory 总是相同的地址