Unicode 代码页 1200、1201、12000、12001 的 MultiByteToWideChar
MultiByteToWideChar for Unicode code pages 1200, 1201, 12000, 12001
我有一个函数可以将字符串从各种编码转换为 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
知道为什么以及如何进行这些转换吗?
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 等库。
Windows 完全不支持 UTF-32,您必须手动实现。
MultiByteToWideChar()
不支持 从 UTF-16 或 UTF-32 的转换。另一方面,对于代码页 1200 和 1201,您的输入数据是 UTF-16 中的 already。 MultiByteToWideChar()
输出 UTF-16LE 数据,因此对于代码页 1200 只需 return 按原样输入数据,对于代码页 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, ...)
我有一个函数可以将字符串从各种编码转换为 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
知道为什么以及如何进行这些转换吗?
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 等库。
Windows 完全不支持 UTF-32,您必须手动实现。
MultiByteToWideChar()
不支持 从 UTF-16 或 UTF-32 的转换。另一方面,对于代码页 1200 和 1201,您的输入数据是 UTF-16 中的 already。 MultiByteToWideChar()
输出 UTF-16LE 数据,因此对于代码页 1200 只需 return 按原样输入数据,对于代码页 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, ...)