MultiByteToWideChar 用于 Unicode 代码页 1200、1201、12000、12001

MultiByteToWideChar for Unicode code pages 1200, 1201, 12000, 12001

本文关键字:1201 12000 12001 1200 用于 Unicode 代码 MultiByteToWideChar      更新时间:2023-10-16

我有一个函数,可以将各种编码中的字符串转换为Windows内部使用的Unicode-16。为此,我使用了MultiByteToWideChar API。但我刚刚发现以下内容:

//See how much data do we need?
//UNIT nCodePage = 1201;  // just as an example
UINT nchLen = ::MultiByteToWideChar(nCodePage, 0, 
    pByteArrayToConvert, ncbSzByteArrayToConvert, NULL, 0);
if(!nchLen)
{
    //Failed
}

以下 Unicode 代码页失败,错误代码为 ERROR_INVALID_PARAMETER (87):

> 1200  utf-16  Unicode UTF-16, little endian byte order
> 1201  unicodeFFFE Unicode UTF-16, big endian byte order
> 12000 utf-32  Unicode UTF-32, little endian byte order
> 12001 utf-32BE    Unicode UTF-32, big endian byte order

知道为什么以及如何进行这些转换吗?

Windows根本不支持UTF-32,您必须手动实现。

MultiByteToWideChar() 不支持从 UTF-16 或 UTF-32 进行转换。 另一方面,对于代码页 1200 和 1201,输入数据已经采用 UTF-16。 MultiByteToWideChar()输出 UTF-16LE 数据,因此对于代码页 1200,只需按原样返回输入数据,对于代码页 1201,只需交换每个 UTF-16 代码单元的字节序。 但是对于代码页 12000 和 12001,您必须手动转换数据(如果使用 C++11 或更高版本,则必须使用第三方库或 STL 的内置 UTF-16/32 转换)。

尝试这样的事情:

UINT BytesToUTF16LE(UINT CodePage, LPCSTR lpMultiByteStr, int cbMultiByte, LPWSTR lpWideCharStr, int cchWideChar)
{
    UINT nchLen;
    switch (nCodePage)
    {
        case 1200: // UTF-16LE
        case 1201: // UTF-16BE
        {
            if ((!lpMultiByteStr) || (cbMultiByte < 0) || (cchWideChar < 0))
            {
                ::SetLastError(ERROR_INVALID_PARAMETER);
                return 0;
            }
            cbMultiByte /= 2;
            nchLen = cbMultiByte;
            if (lpWideCharStr)
            {
                if (cchWideChar < nchLen)
                {
                    ::SetLastError(ERROR_INSUFFICIENT_BUFFER);
                    return 0;
                }
                if (nCodePage == 1200)
                    CopyMemory(lpWideCharStr, lpMultiByteStr, nchLen * 2);
                else
                {
                    UINT16 pCodeUnits = (UINT16) lpMultiByteStr;
                    for (int i = 0; i < cbMultiByte; ++i)
                    {
                        lpWideCharStr[i] = (WCHAR) (
                            ((pCodeUnits[i] << 8) & 0xFF00) |
                            ((pCodeUnits[i] >> 8) & 0x00FF)
                        );
                    }
                }
            }
            ::SetLastError(0);
            break;
        }
        case 12000: // UTF-32LE
        case 12001: // UTF-32BE
        {
            if ((!lpMultiByteStr) || (cbMultiByte < 0) || (cchWideChar < 0))
            {
                ::SetLastError(ERROR_INVALID_PARAMETER);
                return 0;
            }
            PUINT32 pCodePoints = (PUINT32) lpMultiByteStr;
            cbMultiByte /= 4;
            nchLen = 0;
            for(int i = 0; i < cbMultiByte; ++i)
            {
                UINT32 CodePoint = pCodePoints[i];
                if (nCodePage == 12001)
                {
                    CodePoint = (
                        ((CodePoint >> 24) & 0x000000FF) |
                        ((CodePoint >> 8 ) & 0x0000FF00) |
                        ((CodePoint << 8 ) & 0x00FF0000) |
                        ((CodePoint << 24) & 0xFF000000)
                    );
                }
                if (CodePoint < 0x10000)
                {
                    if (lpWideCharStr)
                    {
                        if (cchWideChar < 1)
                        {
                            ::SetLastError(ERROR_INSUFFICIENT_BUFFER);
                            return 0;
                        }
                        *lpWideCharStr++ = (WCHAR) (CodePoint & 0xFFFF);
                        --cchWideChar;
                    }
                    ++nchLen;
                }
                else if (CodePoint <= 0x10FFFF)
                {
                    if (lpWideCharStr)
                    {
                        if (cchWideChar < 2)
                        {
                            ::SetLastError(ERROR_INSUFFICIENT_BUFFER);
                            return 0;
                        }
                        CodePoint -= 0x10000;
                        *lpWideCharStr++ = (WCHAR) (0xD800 + ((CodePoint >> 10) & 0x3FF));
                        *lpWideCharStr++ = (WCHAR) (0xDC00 + (CodePoint & 0x3FF));
                        cchWideChar -= 2;
                    }
                    nchLen += 2;
                }
                else
                {
                    ::SetLastError(ERROR_NO_UNICODE_TRANSLATION);
                    return 0;
                }
            }
            ::SetLastError(0);
            break;
        }
        default:
            nchLen = ::MultiByteToWideChar(nCodePage, 0, lpMultiByteStr, cbMultiByte, lpWideCharStr, cchWideChar);
            break;
    }
    return nchLen;
}

然后你可以这样做:

UINT nchLen = BytesToUTF16LE(nCodePage, pByteArrayToConvert, ncbSzByteArrayToConvert, NULL, 0)
if ((!nchLen) && (GetLastError() != 0))
{
    //Failed
}
...
BytesToUTF16LE(nCodePage, pByteArrayToConvert, ncbSzByteArrayToConvert, ...)
MultiByteToWideChar不提供

这些转换,因为 UTF-16 和 UTF-32 不是 MBCS 编码。

至于如何转换它们,请执行以下操作:

  • UTF-16LE,无需转换。
  • UTF-16BE,字节交换每个 16 位字符元素。
  • UTF-32LE,将每个 32 位字符元素转换为一个或两个 16 位字符元素。算法描述如下:http://unicode.org/faq/utf_bom.html#utf16-3
  • UTF-32BE,字节
  • 交换每个 32 位字符元素,然后视为 UTF-32LE。

您可以考虑使用 ICU 等库。