Unicode简介
见钱眼开 于2005-3-1
为什么需要Unicode编码?因为ASCII编码无法表示足够多的字符。在ASCII编码中,每个字符用7位表示,可以表示128个不同字符;在Unicode中,每个字符用16位表示,可以表示216即65536个不同字符。
为什么双字节字符集(DBCS:double-byte character set)不能完全满足需要?在双字节字符集中,最初的128个编码(0-127)就是ASCII,从128(0x80)开始,由两个字节组成一个字符。这就带来了附加的编程问题。难以凭字节数决定字符数,难以进行字符串指针操作。而Unicode所有字符都是两字节的,不会带来编程中的问题,唯一不足就是会占用更多的磁盘空间。
在C程序中,使用char类型以1字节长度定义存储1个字符,如:
char c = ‘A’; //变量c大小为1字节,存储内容为0x41
对于Unicode字符,采用wchar_t类型以2个字节长度定义存储1个字符。如:
wchar_t d = L’A’; //变量d大小为2字节,存储内容为0x0041
其实wchar_t和无符号短整型等价,都是16位宽:
typedef unsigned short wchar_t;
至于字符串前的大写字母L,是个修饰符号,告诉编译器要为该字符串每个字符分配2字节内存。
对于宽字符,ANSI C标准提供了新的字符处理库函数,这些函数在string.h和wchar.h中都有声明。例如原strlen函数,对应的宽字符版本函数原型声明如下:
size_t __cdecl wcslen(const wchar_t* );
但这样带来的问题是如何在一个程序中同时可以处理两种字符类型呢?
一个办法是使用Visual C++包含的tchar.h头文件,该头文件不是ANSI C标准一部分,所以每个定义的函数和宏定义前都一个条下划线。如果定义了_UNICODE标识符,进行下列宏定义:
#define _tcslen wcslen
否则定义:
#define _tcslen strlen
在tchar.h头文件中还增加了一种TCHAR类型来解决两种字符类型的问题。如果定义了_UNICODE标识符,进行下列类型等价声明:
typedef wchar_t TCHAR;
#define __T(x) L##x 或者 #define _T(x) L##x 或者 #define _TEXT(x) L##x
否则声明:
typedef char TCHAR;
#define __T(x) x 或者 #define _T (x) x 或者 #define _TEXT(x) x
以上这些都是基于C运行时库的声明定义,在Windows下Microsoft重新定义了新的数据类型和函数,可以在winnt.h头文件中我们可以看到:
typedef char CHAR
typedef wchar_t WCHAR
typedef CHAR *PCHAR,*LPCH,*PCH,*NPSTR,*LPSTR,*PSTR;
typedef CONST CHAR *LPCCH,*PCCH,*LPCSTR,*PCSTR;
typedef WCHAR *PWCHAR,*LPWCH,*PWCH,*NPWSTR,*LPWSTR,*PWSTR;
typedef CONST WCHAR *LPCWCH,*PCWCH,*LPCWSTR,*PCWSTR;
如果定义了UNICODE标识符(没有下划线),则将TCHAR和指向TCHAR指针定义为WCHAR和指向WCHAR指针,否则定义为CHAR和指向CHAR指针。此外,在Winnt头文件中还增加了一个宏,就是将L添加到字符串的第一个引号前。
#ifdef UNICODE
#define __TEXT(quote) L##quote
#else
#define __TEXT(quote) quote
#endif
windows NT完全支持Unicode。因此大多数API函数都存在两种版本声明定义,通常根据UNICODE标识符来定位不同函数入口点。
C运行时库函数已经提供完善的字符处理功能,但为了更方便windows下程序开发的需要,Windows中重新定义了一些类似的字符处理函数。
遗憾的是,printf在windows程序中已经不能使用。我们仍可以使用sprintf和vsprintf。printf函数声明如下:
int printf(const char* pszFormat,…);
首个参数是格式字符串,后面是对应格式字符串的若干个参数
int sprintf(char* pszBuffer,const char* pszFormat,…);
首个参数是字符缓冲区,其他参数和printf函数参数含义一致。Sprintf函数将结果写入字符缓冲区
int __cdecl vsprintf(char * pszBuffer, const char * pszFormat, va_list ArgsList);
首个参数是字符缓冲区,第二个参数是格式字符串,最后一个参数是指向格式化参数数组的指针,实际上该指针指向在堆栈中供函数调用的变量。
Vsprintf函数使用实例如下:
int __cdecl ShowMessage(char* pzsCaption,char* pszFormat,…)
{
char* pszBuffer[256];
va_list pArgList;
va_start(pArgList,pszFormat);
vsprintf(pszBuffer,pszFormat,pArgList);
va_end(pArgList);
return MesageBox(NULL,pszBuffer,szCaption,0);
}
使用sprintf和vsprintf函数的问题是无法保证字符缓冲区不溢出。
_snprintf函数通过增加一个缓冲区可容纳字符数大小参数解决了以上问题。