Unicode与字符集函数
近日闲来无事,前一阵子又被Unicode搞的焦头烂额,于是想看看MSDN吧!英文的看起来真费劲,为了以后省点劲,翻译总结了一下,备查。英文水平有限,如有出入,请参见MSDN。
第一个就是宽字符到多字节字符转换函数,函数原型如下:
int WideCharToMultiByte(
UINT CodePage,
DWORD dwFlags,
LPCWSTR lpWideCharStr,
int cchWideChar,
LPSTR lpMultiByteStr,
int cbMultiByte,
LPCSTR lpDefaultChar,
LPBOOL lpUsedDefaultChar
);
此函数把宽字符串转换成指定的新的字符串,如ANSI,UTF8等,新字符串不必是多字节字符集。
参数:
CodePage: 指定要转换成的字符集代码页,它可以是任何已经安装的或系统自带的字符集,你也可以使用如下所示代码页之一。
CP_ACP 当前系统ANSI代码页
CP_MACCP 当前系统Macintosh代码页
CP_OEMCP 当前系统OEM代码页,一种原始设备制造商硬件扫描码
CP_SYMBOL Symbol代码页,用于Windows 2000及以后版本,我不明白是什么
CP_THREAD_ACP 当前线程ANSI代码页,用于Windows 2000及以后版本,我不明白是什么
CP_UTF7 UTF-7,设置此值时lpDefaultChar和lpUsedDefaultChar都必须为NULL
CP_UTF8 UTF-8,设置此值时lpDefaultChar和lpUsedDefaultChar都必须为NULL
我想最常用的应该是CP_ACP和CP_UTF8了,前者将宽字符转换为ANSI,后者转换为UTF8。
dwFlags: 指定如何处理没有转换的字符, 但不设此参数函数会运行的更快一些,我都是把它设为0。 可设的值如下表所示:
WC_NO_BEST_FIT_CHARS 把不能直接转换成相应多字节字符的Unicode字符转换成lpDefaultChar指定的默认字符。也就是说,如果把Unicode转换成多字节字符,然后再转换回来,你并不一定得到相同的Unicode字符,因为这期间可能使用了默认字符。此选项可以单独使用,也可以和其他选项一起使用。
WC_COMPOSITECHECK 把合成字符转换成预制的字符。它可以与后三个选项中的任何一个组合使用,如果没有与他们中的任何一个组合,则与选项WC_SEPCHARS相同。
WC_ERR_INVALID_CHARS 此选项会致使函数遇到无效字符时失败返回,并且GetLastError会返回错误码ERROR_NO_UNICODE_TRANSLATION。否则函数会自动丢弃非法字符。此选项只能用于UTF8。
WC_DISCARDNS 转换时丢弃不占空间的字符,与WC_COMPOSITECHECK一起使用
WC_SEPCHARS 转换时产生单独的字符,此是默认转换选项,与WC_COMPOSITECHECK一起使用
WC_DEFAULTCHAR 转换时使用默认字符代替例外的字符,(最常见的如’?’),与WC_COMPOSITECHECK一起使用。
当指定WC_COMPOSITECHECK时,函数会将合成字符转换成预制字符。合成字符由一个基字符和一个不占空间的字符(如欧洲国家及汉语拼音的音标)组成,每一个都有不同的字符值。预制字符有一个用于表示基字符和不占空间字符的合成体的单一的字符值。
当指定WC_COMPOSITECHECK选项时,也可以使用上表列出的最后3个选项来定制预制字符的转换规则。这些选项决定了函数在遇到宽字符串的合成字符没有对应的预制字符时的行为,他们与WC_COMPOSITECHECK一起使用,如果都没有指定,函数默认WC_SEPCHARS。
对于下列代码页,dwFlags必须为0,否则函数返回错误码ERROR_INVALID_FLAGS。
50220 50221 50222 50225 50227 50229 52936 54936 57002到57011 65000(UTF7) 42(Symbol)
对于UTF8,dwFlags必须为0或WC_ERR_INVALID_CHARS,否则函数都将失败返回并设置错误码ERROR_INVALID_FLAGS,你可以调用GetLastError获得。
lpWideCharStr: 待转换的宽字符串。
cchWideChar: 待转换宽字符串的长度,-1表示转换到字符串结尾。
lpMultiByteStr: 接收转换后输出新串的缓冲区。
cbMultiByte: 输出缓冲区大小,如果为0,lpMultiByteStr将被忽略,函数将返回所需缓冲区大小而不使用lpMultiByteStr。
lpDefaultChar: 指向字符的指针, 在指定编码里找不到相应字符时使用此字符作为默认字符代替。 如果为NULL则使用系统默认字符。对于要求此参数为NULL的dwFlags而使用此参数,函数将失败返回并设置错误码ERROR_INVALID_PARAMETER。
lpUsedDefaultChar:开关变量的指针,用以表明是否使用过默认字符。对于要求此参数为NULL的dwFlags而使用此参数,函数将失败返回并设置错误码ERROR_INVALID_PARAMETER。lpDefaultChar和lpUsedDefaultChar都设为NULL,函数会更快一些。
返回值: 如果函数成功,且cbMultiByte非0,返回写入lpMultiByteStr的字节数(包括字符串结尾的null);cbMultiByte为0,则返回转换所需
字节数。函数失败,返回0。
注意:函数WideCharToMultiByte使用不当,会给影响程序的安全。调用此函数会很容易导致内存泄漏,因为lpWideCharStr指向的输入缓冲区大小是宽字符数,而lpMultiByteStr指向的输出缓冲区大小是字节数。为了避免内存泄漏,应确保为输出缓冲区指定合适的大小。我的方法是先使cbMultiByte为0调用WideCharToMultiByte一次以获得所需缓冲区大小,为缓冲区分配空间,然后再次调用WideCharToMultiByte填充缓冲区,详见下面的代码。另外,从Unicode UTF16向非Unicode字符集转换可能会导致数据丢失,因为该字符集可能无法找到表示特定Unicode数据的字符。
wchar_t* pwszUnicode = "Holle, word! 你好,中国!";
int iSize;
char* pszMultiByte;
iSize = WideCharToMultiByte(CP_ACP, 0, pwszUnicode, -1, NULL, 0, NULL, NULL);
pszMultiByte = (char*)malloc((iSize+1)/**sizeof(char)*/);
WideCharToMultiByte(CP_ACP, 0, pwszUnicode, -1, pszMultiByte, iSize, NULL, NULL);
第二个是多字节字符到宽字符转换函数,函数原型如下:
> int MultiByteToWideChar(
UINT CodePage,
DWORD dwFlags,
LPCSTR lpMultiByteStr,
int cbMultiByte,
LPWSTR lpWideCharStr,
int cchWideChar
);
此函数把多字节字符串转换成宽字符串(Unicode),待转换的字符串并不一定是多字节的。
此函数的参数,返回值及注意事项参见上面函数WideCharToMultiByte的说明,这里只对dwFlags做简单解释。
dwFlags: 指定是否转换成预制字符或合成的宽字符,对控制字符是否使用像形文字,以及怎样处理无效字符。
MB_PRECOMPOSED 总是使用预制字符,即有单个预制字符时,就不会使用分解的基字符和不占空间字符。此为函数的默认选项,不能和MB_COMPOSITE合用
MB_COMPOSITE 总是使用分解字符,即总是使用基字符+不占空间字符的方式
MB_ERR_INVALID_CHARS 设置此选项,函数遇到非法字符就失败并返回错误码ERROR_NO_UNICODE_TRANSLATION,否则丢弃非法字符
MB_USEGLYPHCHARS 使用像形字符代替控制字符
对于下列代码页,dwFlags必须为0,否则函数返回错误码ERROR_INVALID_FLAGS。
50220 50221 50222 50225 50227 50229 52936 54936 57002到57011 65000(UTF7) 42(Symbol)
对于UTF8,dwFlags必须为0或MB_ERR_INVALID_CHARS,否则函数都将失败并返回错误码ERROR_INVALID_FLAGS。
以下函数我没用过,只简要说明之。
int GetTextCharset( HDC hdc );
此函数获取当前选进的设备描述表的字符集,等同于GetTextCharsetInfo(hdc, NULL, 0)。
返回值: 成功返回字符集标识,失败返回DEFAULT_CHARSET。
Windows定义的字符集标识有:
ANSI_CHARSET
BALTIC_CHARSET
CHINESEBIG5_CHARSET
DEFAULT_CHARSET
EASTEUROPE_CHARSET
GB2312_CHARSET
GREEK_CHARSET
HANGUL_CHARSET
MAC_CHARSET
OEM_CHARSET
RUSSIAN_CHARSET
SHIFTJIS_CHARSET
SYMBOL_CHARSET
TURKISH_CHARSET
VIETNAMESE_CHARSET
JOHAB_CHARSET(韩语版)
ARABIC_CHARSET(中东版)
HEBREW_CHARSET(中东版)
THAI_CHARSET(泰国版)
int GetTextCharsetInfo(
HDC hdc,
LPFONTSIGNATURE lpSig,
DWORD dwFlags
);
此函数获取当前选进设备描述表的字体的字符集信息。
参数:
lpSig:指向FONTSIGNATURE结构的指针。如果当前选进设备的是TureType字体,函数会填充FONTSIGNATURE结构。否则FONTSIGNATURE结构被填充为0,而应使用TranslateCharsetInfo来获取一般字符集信息,如果你不需要字体标志信息,可将此参数设为NULL,此时相当于调用GetTextCharset。
FONTSIGNATURE结构包括代码页和Unicode子域的信息,Unicode子域是指定字体的字型轮廓。该结构定义如下:
typedef struct tagFONTSIGNATURE {
DWORD fsUsb[4];
DWORD fsCsb[2];
} FONTSIGNATURE, *PFONTSIGNATURE;
fsUsb 是一个128位的Unicode子集标志位(Unicode Subset Bitfield),用以标识最多126个Unicode子域。除了两个最高有效位外,每一位代表一个子域。最高位总是1,表示此位是一个字体标志,次高位保留,必须位0。Unicode子域根据ISO 10646标准制定。
fsCsb 是一个64位的代码页标志位,用以标识制定的字符集或代码页。代码页由低32位标识,而高32位用于非Windows代码页。
dwFlags:保留,必须为0。
返回值:成功返回字符集标识,失败返回DEFAULT_CHARSET。
BOOL TranslateCharsetInfo(
DWORD* pSrc,
LPCHARSETINFO lpCs,
DWORD dwFlags
);
此函数根据指定字符集,代码页或字体标识转换并设置目标结体的值。
参数:
lpSrc:如果dwFlags是TCI_SRCFONTSIG,此参数是FONTSIGNATURE结构的fsCsb成员地址,否则是一个DWORD值。
lpCs: 指向要填充的CHARSETINFO结构。
CHARSETINFO结构定义如下:
typedef struct {
UINT ciCharset; // 字符集标识
UINT ciACP; // ANSI代码页标识
FONTSIGNATURE fs;
} CHARSETINFO, *PCHARSETINFO;
dwFlags: 指定如何转换。可以是下列标识之一:
TCI_SRCCHARSET lpSrc的低字是字符集标识,高字为0
TCI_SRCCODEPAGE lpSrc的低字是代码页标识,高字为0
TCI_SRCFONTSIG lpSrc是FONTSIGNATURE的代码页标志位部分,即FONTSIGNATURE的fsCsb成员地址
TCI_SRCLOCALE lpSrc是键盘布局的LCID或LANGID,如果是LANGID,其值存在lpSrc的低字
BOOL IsDBCSLeadByte( BYTE TestChar );
此函数使用当前WindowsANSI代码页判断指定字节是不是一个潜在的双字节字符(DBCS)的引导字节。若使用其他代码页,请用IsDBCSLeadByteEx函数。
BOOL IsDBCSLeadByteEx(
UINT CodePage,
BYTE TestChar
);
此函数判断指定字节是不是一个潜在的双字节字符(DBCS)的引导字节。
CodePage可用的值有:CP_ACP,CP_MACCP,CP_OEMCP,CP_THREAD_ACP。
BOOL IsTextUnicode(
CONST VOID* pBuffer,
int cb,
LPINT lpi
);
此函数判断指定缓冲区里是否可能会有某种格式的Unicode文本。
参数:
lpi: 输入时指明测试项,输出时测试位返回各项测试结果:通过测试返回1,测试失败返回0。如果为NULL,则函数会测试所有可能的项。它可以是以下值的一个或多个:
IS_TEXT_UNICODE_ASCII16 缓冲区文本是Unicode,并且仅有0扩展的ASCII字符
IS_TEXT_UNICODE_REVERSE_ASCII16 同上,只是Unicode文本是字节翻转的(与BOM有关)
IS_TEXT_UNICODE_STATISTICS 缓冲区文本可能是Unicode,由于它采用统计分析法,所以并不能确保准确
IS_TEXT_UNICODE_REVERSE_STATISTICS 同上,只是可能的Unicode文本是字节翻转的
IS_TEXT_UNICODE_CONTROLS 缓冲区文本含有Unicode表示的一个或多个非打印控制字符,如RETURN, LINEFEED, SPACE, CJK_SPACE, TAB。
IS_TEXT_UNICODE_REVERSE_CONTROLS 同上,只是Unicode字符是字节翻转的
IS_TEXT_UNICODE_BUFFER_TOO_SMALL 缓冲区里的文本太少,不足以作出有意义的分析
IS_TEXT_UNICODE_SIGNATURE 缓冲区文本含有Unicode字节顺序码(BOM)0XFEFF作为他的第一个字符
IS_TEXT_UNICODE_REVERSE_SIGNATURE 缓冲区文本含有Unicode反向字节顺序码0XFFFE作为他的第一个字符
IS_TEXT_UNICODE_ILLEGAL_CHARS 缓冲区文本含有Unicode非法字符,如内嵌BOM,UNICODE_NUL,CRLF或0XFFFF
IS_TEXT_UNICODE_ODD_LENGTH 缓冲区字符数为奇数,Unicode字符串长度都是偶数
IS_TEXT_UNICODE_NULL_BYTES 缓冲区含有null字节,表示非ASCII文本
IS_TEXT_UNICODE_UNICODE_MASK IS_TEXT_UNICODE_ASCII16 | IS_TEXT_UNICODE_STATISTICS | IS_TEXT_UNICODE_CONTROLS | IS_TEXT_UNICODE_SIGNATURE
IS_TEXT_UNICODE_REVERSE_MASK IS_TEXT_UNICODE_REVERSE_ASCII16 | IS_TEXT_UNICODE_REVERSE_STATISTICS | IS_TEXT_UNICODE_REVERSE_CONTROLS | IS_TEXT_UNICODE_REVERSE_SIGNATURE.
IS_TEXT_UNICODE_NOT_UNICODE_MASK IS_TEXT_UNICODE_ILLEGAL_CHARS | IS_TEXT_UNICODE_ODD_LENGTH和两个当前没有使用的标志位
IS_TEXT_UNICODE_NOT_ASCII_MASK IS_TEXT_UNICODE_NULL_BYTES 和三个当前没有使用的标志位
还有三个Windows不推荐使用的函数:
NlsDllCodePageTranslation,UnicodeToBytes和BytesToUnicode,这里就不作介绍了。
PS:在VC中编译定义了_UNICODE的代码时经常会出现如下连接错误:
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain @16
Debug/Test.exe : fatal error LNK1120: 1 unresolved externals
解决办法如下:
在Project->Settings->Link选项下选择Output, 然后在Entry里输入:wWinMainCRTStartup即可。